diff --git a/flang/lib/Semantics/check-acc-structure.h b/flang/lib/Semantics/check-acc-structure.h --- a/flang/lib/Semantics/check-acc-structure.h +++ b/flang/lib/Semantics/check-acc-structure.h @@ -14,13 +14,12 @@ #ifndef FORTRAN_SEMANTICS_CHECK_ACC_STRUCTURE_H_ #define FORTRAN_SEMANTICS_CHECK_ACC_STRUCTURE_H_ +#include "check-directive-structure.h" #include "flang/Common/enum-set.h" #include "flang/Parser/parse-tree.h" #include "flang/Semantics/semantics.h" #include "llvm/Frontend/OpenACC/ACC.h.inc" -#include - using AccDirectiveSet = Fortran::common::EnumSet; @@ -32,9 +31,16 @@ namespace Fortran::semantics { -class AccStructureChecker : public virtual BaseChecker { +class AccStructureChecker + : public DirectiveStructureChecker { public: - AccStructureChecker(SemanticsContext &context) : context_{context} {} + AccStructureChecker(SemanticsContext &context) + : DirectiveStructureChecker(context, +#define GEN_FLANG_DIRECTIVE_CLAUSE_MAP +#include "llvm/Frontend/OpenACC/ACC.cpp.inc" + ) { + } // Construct and directives void Enter(const parser::OpenACCBlockConstruct &); @@ -100,103 +106,13 @@ void Enter(const parser::AccClause::Write &); private: -#define GEN_FLANG_DIRECTIVE_CLAUSE_MAP -#include "llvm/Frontend/OpenACC/ACC.cpp.inc" - - struct AccContext { - AccContext(parser::CharBlock source, llvm::acc::Directive d) - : directiveSource{source}, directive{d} {} - - parser::CharBlock directiveSource{nullptr}; - parser::CharBlock clauseSource{nullptr}; - llvm::acc::Directive directive; - AccClauseSet allowedClauses{}; - AccClauseSet allowedOnceClauses{}; - AccClauseSet allowedExclusiveClauses{}; - AccClauseSet requiredClauses{}; - - const parser::AccClause *clause{nullptr}; - std::multimap clauseInfo; - std::list actualClauses; - }; - - // back() is the top of the stack - AccContext &GetContext() { - CHECK(!accContext_.empty()); - return accContext_.back(); - } - - void SetContextClause(const parser::AccClause &clause) { - GetContext().clauseSource = clause.source; - GetContext().clause = &clause; - } - - void SetContextClauseInfo(llvm::acc::Clause type) { - GetContext().clauseInfo.emplace(type, GetContext().clause); - } - - void AddClauseToCrtContext(llvm::acc::Clause type) { - GetContext().actualClauses.push_back(type); - } - - const parser::AccClause *FindClause(llvm::acc::Clause type) { - auto it{GetContext().clauseInfo.find(type)}; - if (it != GetContext().clauseInfo.end()) { - return it->second; - } - return nullptr; - } - - void PushContext(const parser::CharBlock &source, llvm::acc::Directive dir) { - accContext_.emplace_back(source, dir); - } - - void SetClauseSets(llvm::acc::Directive dir) { - accContext_.back().allowedClauses = directiveClausesTable[dir].allowed; - accContext_.back().allowedOnceClauses = - directiveClausesTable[dir].allowedOnce; - accContext_.back().allowedExclusiveClauses = - directiveClausesTable[dir].allowedExclusive; - accContext_.back().requiredClauses = - directiveClausesTable[dir].requiredOneOf; - } - void PushContextAndClauseSets( - const parser::CharBlock &source, llvm::acc::Directive dir) { - PushContext(source, dir); - SetClauseSets(dir); - } - - void SayNotMatching(const parser::CharBlock &, const parser::CharBlock &); - - template void CheckMatching(const B &beginDir, const B &endDir) { - const auto &begin{beginDir.v}; - const auto &end{endDir.v}; - if (begin != end) { - SayNotMatching(beginDir.source, endDir.source); - } - } - - // Check that only clauses in set are after the specific clauses. - void CheckOnlyAllowedAfter(llvm::acc::Clause clause, AccClauseSet set); - void CheckRequireAtLeastOneOf(); - void CheckAllowed(llvm::acc::Clause clause); - void CheckAtLeastOneClause(); - void CheckNotAllowedIfClause(llvm::acc::Clause clause, AccClauseSet set); - std::string ContextDirectiveAsFortran(); void CheckNoBranching(const parser::Block &block, const llvm::acc::Directive directive, const parser::CharBlock &directiveSource) const; - void RequiresConstantPositiveParameter( - const llvm::acc::Clause &clause, const parser::ScalarIntConstantExpr &i); - void OptionalConstantPositiveParameter(const llvm::acc::Clause &clause, - const std::optional &o); - - SemanticsContext &context_; - std::vector accContext_; // used as a stack - - std::string ClauseSetToString(const AccClauseSet set); + llvm::StringRef getClauseName(llvm::acc::Clause clause) override; + llvm::StringRef getDirectiveName(llvm::acc::Directive directive) override; }; } // namespace Fortran::semantics diff --git a/flang/lib/Semantics/check-acc-structure.cpp b/flang/lib/Semantics/check-acc-structure.cpp --- a/flang/lib/Semantics/check-acc-structure.cpp +++ b/flang/lib/Semantics/check-acc-structure.cpp @@ -132,7 +132,7 @@ default: break; } - accContext_.pop_back(); + dirContext_.pop_back(); } void AccStructureChecker::CheckNoBranching(const parser::Block &block, @@ -152,7 +152,7 @@ const parser::OpenACCStandaloneDeclarativeConstruct &) { // Restriction - 2075 CheckAtLeastOneClause(); - accContext_.pop_back(); + dirContext_.pop_back(); } void AccStructureChecker::Enter(const parser::OpenACCCombinedConstruct &x) { @@ -185,12 +185,7 @@ default: break; } - accContext_.pop_back(); -} - -std::string AccStructureChecker::ContextDirectiveAsFortran() { - return parser::ToUpperCaseLetters( - llvm::acc::getOpenACCDirectiveName(GetContext().directive).str()); + dirContext_.pop_back(); } void AccStructureChecker::Enter(const parser::OpenACCLoopConstruct &x) { @@ -211,7 +206,7 @@ {llvm::acc::Clause::ACCC_gang, llvm::acc::Clause::ACCC_vector, llvm::acc::Clause::ACCC_worker}); } - accContext_.pop_back(); + dirContext_.pop_back(); } void AccStructureChecker::Enter(const parser::OpenACCStandaloneConstruct &x) { @@ -238,7 +233,7 @@ default: break; } - accContext_.pop_back(); + dirContext_.pop_back(); } void AccStructureChecker::Enter(const parser::OpenACCRoutineConstruct &x) { @@ -250,7 +245,7 @@ // Restriction - 2407-2408 CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type, routineOnlyAllowedAfterDeviceTypeClauses); - accContext_.pop_back(); + dirContext_.pop_back(); } // Clause checkers @@ -348,154 +343,13 @@ } } -void AccStructureChecker::CheckAllowed(llvm::acc::Clause clause) { - if (!GetContext().allowedClauses.test(clause) && - !GetContext().allowedOnceClauses.test(clause) && - !GetContext().allowedExclusiveClauses.test(clause) && - !GetContext().requiredClauses.test(clause)) { - context_.Say(GetContext().clauseSource, - "%s clause is not allowed on the %s directive"_err_en_US, - parser::ToUpperCaseLetters( - llvm::acc::getOpenACCClauseName(clause).str()), - parser::ToUpperCaseLetters(GetContext().directiveSource.ToString())); - return; - } - if ((GetContext().allowedOnceClauses.test(clause) || - GetContext().allowedExclusiveClauses.test(clause)) && - FindClause(clause)) { - context_.Say(GetContext().clauseSource, - "At most one %s clause can appear on the %s directive"_err_en_US, - parser::ToUpperCaseLetters( - llvm::acc::getOpenACCClauseName(clause).str()), - parser::ToUpperCaseLetters(GetContext().directiveSource.ToString())); - return; - } - if (GetContext().allowedExclusiveClauses.test(clause)) { - std::vector others; - GetContext().allowedExclusiveClauses.IterateOverMembers( - [&](llvm::acc::Clause o) { - if (FindClause(o)) { - others.emplace_back(o); - } - }); - for (const auto &e : others) { - context_.Say(GetContext().clauseSource, - "%s and %s clauses are mutually exclusive and may not appear on the " - "same %s directive"_err_en_US, - parser::ToUpperCaseLetters( - llvm::acc::getOpenACCClauseName(clause).str()), - parser::ToUpperCaseLetters(llvm::acc::getOpenACCClauseName(e).str()), - parser::ToUpperCaseLetters(GetContext().directiveSource.ToString())); - } - if (!others.empty()) { - return; - } - } - SetContextClauseInfo(clause); - AddClauseToCrtContext(clause); -} - -void AccStructureChecker::CheckOnlyAllowedAfter( - llvm::acc::Clause clause, AccClauseSet set) { - bool enforceCheck = false; - for (auto cl : GetContext().actualClauses) { - if (cl == clause) { - enforceCheck = true; - continue; - } else if (enforceCheck && !set.test(cl)) { - auto parserClause = GetContext().clauseInfo.find(cl); - context_.Say(parserClause->second->source, - "Clause %s is not allowed after clause %s on the %s " - "directive"_err_en_US, - parser::ToUpperCaseLetters(llvm::acc::getOpenACCClauseName(cl).str()), - parser::ToUpperCaseLetters( - llvm::acc::getOpenACCClauseName(clause).str()), - ContextDirectiveAsFortran()); - } - } -} - -void AccStructureChecker::CheckRequireAtLeastOneOf() { - for (auto cl : GetContext().actualClauses) { - if (GetContext().requiredClauses.test(cl)) - return; - } - // No clause matched in the actual clauses list - context_.Say(GetContext().directiveSource, - "At least one of %s clause must appear on the %s directive"_err_en_US, - ClauseSetToString(GetContext().requiredClauses), - ContextDirectiveAsFortran()); -} - -void AccStructureChecker::CheckAtLeastOneClause() { - if (GetContext().actualClauses.empty()) { - context_.Say(GetContext().directiveSource, - "At least one clause is required on the %s directive"_err_en_US, - ContextDirectiveAsFortran()); - } -} - -// Enforce restriction where clauses in the given set are not allowed if the -// given clause appears. -void AccStructureChecker::CheckNotAllowedIfClause( - llvm::acc::Clause clause, AccClauseSet set) { - if (std::find(GetContext().actualClauses.begin(), - GetContext().actualClauses.end(), - clause) == GetContext().actualClauses.end()) { - return; // Clause is not present - } - - for (auto cl : GetContext().actualClauses) { - if (set.test(cl)) { - context_.Say(GetContext().directiveSource, - "Clause %s is not allowed if clause %s appears on the %s directive"_err_en_US, - parser::ToUpperCaseLetters(llvm::acc::getOpenACCClauseName(cl).str()), - parser::ToUpperCaseLetters( - llvm::acc::getOpenACCClauseName(clause).str()), - ContextDirectiveAsFortran()); - } - } -} - -void AccStructureChecker::RequiresConstantPositiveParameter( - const llvm::acc::Clause &clause, const parser::ScalarIntConstantExpr &i) { - if (const auto v{GetIntValue(i)}) { - if (*v <= 0) { - context_.Say(GetContext().clauseSource, - "The parameter of the %s clause on the %s directive must be " - "a constant positive integer expression"_err_en_US, - parser::ToUpperCaseLetters( - llvm::acc::getOpenACCClauseName(clause).str()), - ContextDirectiveAsFortran()); - } - } -} - -void AccStructureChecker::OptionalConstantPositiveParameter( - const llvm::acc::Clause &clause, - const std::optional &o) { - if (o != std::nullopt) { - RequiresConstantPositiveParameter(clause, o.value()); - } -} - -std::string AccStructureChecker::ClauseSetToString(const AccClauseSet set) { - std::string list; - set.IterateOverMembers([&](llvm::acc::Clause o) { - if (!list.empty()) - list.append(", "); - list.append( - parser::ToUpperCaseLetters(llvm::acc::getOpenACCClauseName(o).str())); - }); - return list; +llvm::StringRef AccStructureChecker::getClauseName(llvm::acc::Clause clause) { + return llvm::acc::getOpenACCClauseName(clause); } -void AccStructureChecker::SayNotMatching( - const parser::CharBlock &beginSource, const parser::CharBlock &endSource) { - context_ - .Say(endSource, "Unmatched %s directive"_err_en_US, - parser::ToUpperCaseLetters(endSource.ToString())) - .Attach(beginSource, "Does not match directive"_en_US); +llvm::StringRef AccStructureChecker::getDirectiveName( + llvm::acc::Directive directive) { + return llvm::acc::getOpenACCDirectiveName(directive); } } // namespace Fortran::semantics diff --git a/flang/lib/Semantics/check-directive-structure.h b/flang/lib/Semantics/check-directive-structure.h new file mode 100644 --- /dev/null +++ b/flang/lib/Semantics/check-directive-structure.h @@ -0,0 +1,389 @@ +//===-- lib/Semantics/check-directive-structure.h ---------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Directive structure validity checks common to OpenMP, OpenACC and other +// directive language. + +#ifndef FORTRAN_SEMANTICS_CHECK_DIRECTIVE_STRUCTURE_H_ +#define FORTRAN_SEMANTICS_CHECK_DIRECTIVE_STRUCTURE_H_ + +#include "flang/Common/enum-set.h" +#include "flang/Semantics/semantics.h" +#include "flang/Semantics/tools.h" + +#include + +namespace Fortran::semantics { + +template struct DirectiveClauses { + const common::EnumSet allowed; + const common::EnumSet allowedOnce; + const common::EnumSet allowedExclusive; + const common::EnumSet requiredOneOf; +}; + +// Generic structure checker for directives/clauses language such as OpenMP +// and OpenACC. +// typename D is the directive enumeration. +// tyepname C is the clause enumeration. +// typename PC is the parser class defined in parse-tree.h for the clauses. +template +class DirectiveStructureChecker : public virtual BaseChecker { +public: + DirectiveStructureChecker(SemanticsContext &context, + std::unordered_map> + directiveClausesMap) + : context_{context}, directiveClausesMap_(directiveClausesMap) {} + +protected: + struct DirectiveContext { + DirectiveContext(parser::CharBlock source, D d) + : directiveSource{source}, directive{d} {} + + parser::CharBlock directiveSource{nullptr}; + parser::CharBlock clauseSource{nullptr}; + D directive; + common::EnumSet allowedClauses{}; + common::EnumSet allowedOnceClauses{}; + common::EnumSet allowedExclusiveClauses{}; + common::EnumSet requiredClauses{}; + + const PC *clause{nullptr}; + std::multimap clauseInfo; + std::list actualClauses; + }; + + // back() is the top of the stack + DirectiveContext &GetContext() { + CHECK(!dirContext_.empty()); + return dirContext_.back(); + } + + void SetContextClause(const PC &clause) { + GetContext().clauseSource = clause.source; + GetContext().clause = &clause; + } + + void ResetPartialContext(const parser::CharBlock &source) { + CHECK(!dirContext_.empty()); + SetContextDirectiveSource(source); + GetContext().allowedClauses = {}; + GetContext().allowedOnceClauses = {}; + GetContext().allowedExclusiveClauses = {}; + GetContext().requiredClauses = {}; + GetContext().clauseInfo = {}; + } + + void SetContextDirectiveSource(const parser::CharBlock &directive) { + GetContext().directiveSource = directive; + } + + void SetContextDirectiveEnum(D dir) { GetContext().directive = dir; } + + void SetContextAllowed(const common::EnumSet &allowed) { + GetContext().allowedClauses = allowed; + } + + void SetContextAllowedOnce( + const common::EnumSet &allowedOnce) { + GetContext().allowedOnceClauses = allowedOnce; + } + + void SetContextAllowedExclusive( + const common::EnumSet &allowedExclusive) { + GetContext().allowedExclusiveClauses = allowedExclusive; + } + + void SetContextRequired(const common::EnumSet &required) { + GetContext().requiredClauses = required; + } + + void SetContextClauseInfo(C type) { + GetContext().clauseInfo.emplace(type, GetContext().clause); + } + + void AddClauseToCrtContext(C type) { + GetContext().actualClauses.push_back(type); + } + + const PC *FindClause(C type) { + auto it{GetContext().clauseInfo.find(type)}; + if (it != GetContext().clauseInfo.end()) { + return it->second; + } + return nullptr; + } + + void PushContext(const parser::CharBlock &source, D dir) { + dirContext_.emplace_back(source, dir); + } + + bool CurrentDirectiveIsNested() { return dirContext_.size() > 0; }; + + void SetClauseSets(D dir) { + dirContext_.back().allowedClauses = directiveClausesMap_[dir].allowed; + dirContext_.back().allowedOnceClauses = + directiveClausesMap_[dir].allowedOnce; + dirContext_.back().allowedExclusiveClauses = + directiveClausesMap_[dir].allowedExclusive; + dirContext_.back().requiredClauses = + directiveClausesMap_[dir].requiredOneOf; + } + void PushContextAndClauseSets(const parser::CharBlock &source, D dir) { + PushContext(source, dir); + SetClauseSets(dir); + } + + void SayNotMatching(const parser::CharBlock &, const parser::CharBlock &); + + template void CheckMatching(const B &beginDir, const B &endDir) { + const auto &begin{beginDir.v}; + const auto &end{endDir.v}; + if (begin != end) { + SayNotMatching(beginDir.source, endDir.source); + } + } + + // Check that only clauses in set are after the specific clauses. + void CheckOnlyAllowedAfter(C clause, common::EnumSet set); + + void CheckRequired(C clause); + + void CheckRequireAtLeastOneOf(); + + void CheckAllowed(C clause); + + void CheckAtLeastOneClause(); + + void CheckNotAllowedIfClause( + C clause, common::EnumSet set); + + std::string ContextDirectiveAsFortran(); + + void RequiresConstantPositiveParameter( + const C &clause, const parser::ScalarIntConstantExpr &i); + + void RequiresPositiveParameter( + const C &clause, const parser::ScalarIntExpr &i); + + void OptionalConstantPositiveParameter( + const C &clause, const std::optional &o); + + virtual llvm::StringRef getClauseName(C clause) { return ""; }; + + virtual llvm::StringRef getDirectiveName(D directive) { return ""; }; + + SemanticsContext &context_; + std::vector dirContext_; // used as a stack + std::unordered_map> + directiveClausesMap_; + + std::string ClauseSetToString(const common::EnumSet set); +}; + +// Check that only clauses included in the given set are present after the given +// clause. +template +void DirectiveStructureChecker::CheckOnlyAllowedAfter( + C clause, common::EnumSet set) { + bool enforceCheck = false; + for (auto cl : GetContext().actualClauses) { + if (cl == clause) { + enforceCheck = true; + continue; + } else if (enforceCheck && !set.test(cl)) { + auto parserClause = GetContext().clauseInfo.find(cl); + context_.Say(parserClause->second->source, + "Clause %s is not allowed after clause %s on the %s " + "directive"_err_en_US, + parser::ToUpperCaseLetters(getClauseName(cl).str()), + parser::ToUpperCaseLetters(getClauseName(clause).str()), + ContextDirectiveAsFortran()); + } + } +} + +// Check that at least one clause is attached to the directive. +template +void DirectiveStructureChecker::CheckAtLeastOneClause() { + if (GetContext().actualClauses.empty()) { + context_.Say(GetContext().directiveSource, + "At least one clause is required on the %s directive"_err_en_US, + ContextDirectiveAsFortran()); + } +} + +template +std::string +DirectiveStructureChecker::ClauseSetToString( + const common::EnumSet set) { + std::string list; + set.IterateOverMembers([&](C o) { + if (!list.empty()) + list.append(", "); + list.append(parser::ToUpperCaseLetters(getClauseName(o).str())); + }); + return list; +} + +// Check that at least one clause in the required set is present on the +// directive. +template +void DirectiveStructureChecker::CheckRequireAtLeastOneOf() { + for (auto cl : GetContext().actualClauses) { + if (GetContext().requiredClauses.test(cl)) + return; + } + // No clause matched in the actual clauses list + context_.Say(GetContext().directiveSource, + "At least one of %s clause must appear on the %s directive"_err_en_US, + ClauseSetToString(GetContext().requiredClauses), + ContextDirectiveAsFortran()); +} + +template +std::string DirectiveStructureChecker::ContextDirectiveAsFortran() { + return parser::ToUpperCaseLetters( + getDirectiveName(GetContext().directive).str()); +} + +// Check that clauses present on the directive are allowed clauses. +template +void DirectiveStructureChecker::CheckAllowed( + C clause) { + if (!GetContext().allowedClauses.test(clause) && + !GetContext().allowedOnceClauses.test(clause) && + !GetContext().allowedExclusiveClauses.test(clause) && + !GetContext().requiredClauses.test(clause)) { + context_.Say(GetContext().clauseSource, + "%s clause is not allowed on the %s directive"_err_en_US, + parser::ToUpperCaseLetters(getClauseName(clause).str()), + parser::ToUpperCaseLetters(GetContext().directiveSource.ToString())); + return; + } + if ((GetContext().allowedOnceClauses.test(clause) || + GetContext().allowedExclusiveClauses.test(clause)) && + FindClause(clause)) { + context_.Say(GetContext().clauseSource, + "At most one %s clause can appear on the %s directive"_err_en_US, + parser::ToUpperCaseLetters(getClauseName(clause).str()), + parser::ToUpperCaseLetters(GetContext().directiveSource.ToString())); + return; + } + if (GetContext().allowedExclusiveClauses.test(clause)) { + std::vector others; + GetContext().allowedExclusiveClauses.IterateOverMembers([&](C o) { + if (FindClause(o)) { + others.emplace_back(o); + } + }); + for (const auto &e : others) { + context_.Say(GetContext().clauseSource, + "%s and %s clauses are mutually exclusive and may not appear on the " + "same %s directive"_err_en_US, + parser::ToUpperCaseLetters(getClauseName(clause).str()), + parser::ToUpperCaseLetters(getClauseName(e).str()), + parser::ToUpperCaseLetters(GetContext().directiveSource.ToString())); + } + if (!others.empty()) { + return; + } + } + SetContextClauseInfo(clause); + AddClauseToCrtContext(clause); +} + +// Enforce restriction where clauses in the given set are not allowed if the +// given clause appears. +template +void DirectiveStructureChecker::CheckNotAllowedIfClause(C clause, + common::EnumSet set) { + if (std::find(GetContext().actualClauses.begin(), + GetContext().actualClauses.end(), + clause) == GetContext().actualClauses.end()) { + return; // Clause is not present + } + + for (auto cl : GetContext().actualClauses) { + if (set.test(cl)) { + context_.Say(GetContext().directiveSource, + "Clause %s is not allowed if clause %s appears on the %s directive"_err_en_US, + parser::ToUpperCaseLetters(getClauseName(cl).str()), + parser::ToUpperCaseLetters(getClauseName(clause).str()), + ContextDirectiveAsFortran()); + } + } +} + +// Check the value of the clause is a constant positive integer. +template +void DirectiveStructureChecker::RequiresConstantPositiveParameter(const C &clause, + const parser::ScalarIntConstantExpr &i) { + if (const auto v{GetIntValue(i)}) { + if (*v <= 0) { + context_.Say(GetContext().clauseSource, + "The parameter of the %s clause must be " + "a constant positive integer expression"_err_en_US, + parser::ToUpperCaseLetters(getClauseName(clause).str())); + } + } +} + +// Check the value of the clause is a constant positive parameter. +template +void DirectiveStructureChecker::OptionalConstantPositiveParameter(const C &clause, + const std::optional &o) { + if (o != std::nullopt) { + RequiresConstantPositiveParameter(clause, o.value()); + } +} + +template +void DirectiveStructureChecker::SayNotMatching( + const parser::CharBlock &beginSource, const parser::CharBlock &endSource) { + context_ + .Say(endSource, "Unmatched %s directive"_err_en_US, + parser::ToUpperCaseLetters(endSource.ToString())) + .Attach(beginSource, "Does not match directive"_en_US); +} + +// Check that at least one of the required clauses is present on the directive. +template +void DirectiveStructureChecker::CheckRequired(C c) { + if (!FindClause(c)) { + context_.Say(GetContext().directiveSource, + "At least one %s clause must appear on the %s directive"_err_en_US, + parser::ToUpperCaseLetters(getClauseName(c).str()), + ContextDirectiveAsFortran()); + } +} + +// Check the value of the clause is a positive parameter. +template +void DirectiveStructureChecker::RequiresPositiveParameter(const C &clause, + const parser::ScalarIntExpr &i) { + if (const auto v{GetIntValue(i)}) { + if (*v <= 0) { + context_.Say(GetContext().clauseSource, + "The parameter of the %s clause must be " + "a positive integer expression"_err_en_US, + parser::ToUpperCaseLetters(getClauseName(clause).str())); + } + } +} + +} // namespace Fortran::semantics + +#endif // FORTRAN_SEMANTICS_CHECK_DIRECTIVE_STRUCTURE_H_ diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h --- a/flang/lib/Semantics/check-omp-structure.h +++ b/flang/lib/Semantics/check-omp-structure.h @@ -14,13 +14,12 @@ #ifndef FORTRAN_SEMANTICS_CHECK_OMP_STRUCTURE_H_ #define FORTRAN_SEMANTICS_CHECK_OMP_STRUCTURE_H_ +#include "check-directive-structure.h" #include "flang/Common/enum-set.h" #include "flang/Parser/parse-tree.h" #include "flang/Semantics/semantics.h" #include "llvm/Frontend/OpenMP/OMPConstants.h" -#include - using OmpDirectiveSet = Fortran::common::EnumSet; @@ -79,9 +78,16 @@ namespace Fortran::semantics { -class OmpStructureChecker : public virtual BaseChecker { +class OmpStructureChecker + : public DirectiveStructureChecker { public: - OmpStructureChecker(SemanticsContext &context) : context_{context} {} + OmpStructureChecker(SemanticsContext &context) + : DirectiveStructureChecker(context, +#define GEN_FLANG_DIRECTIVE_CLAUSE_MAP +#include "llvm/Frontend/OpenMP/OMP.cpp.inc" + ) { + } void Enter(const parser::OpenMPConstruct &); void Enter(const parser::OpenMPLoopConstruct &); @@ -156,116 +162,16 @@ void Enter(const parser::OmpScheduleClause &); private: -#define GEN_FLANG_DIRECTIVE_CLAUSE_MAP -#include "llvm/Frontend/OpenMP/OMP.cpp.inc" - struct OmpContext { - OmpContext(parser::CharBlock source, llvm::omp::Directive d) - : directiveSource{source}, directive{d} {} - parser::CharBlock directiveSource{nullptr}; - parser::CharBlock clauseSource{nullptr}; - llvm::omp::Directive directive; - OmpClauseSet allowedClauses{}; - OmpClauseSet allowedOnceClauses{}; - OmpClauseSet allowedExclusiveClauses{}; - OmpClauseSet requiredClauses{}; - - const parser::OmpClause *clause{nullptr}; - std::multimap clauseInfo; - }; - // back() is the top of the stack - OmpContext &GetContext() { - CHECK(!ompContext_.empty()); - return ompContext_.back(); - } - // reset source location, check information, and - // collected information for END directive - void ResetPartialContext(const parser::CharBlock &source) { - CHECK(!ompContext_.empty()); - SetContextDirectiveSource(source); - GetContext().allowedClauses = {}; - GetContext().allowedOnceClauses = {}; - GetContext().allowedExclusiveClauses = {}; - GetContext().requiredClauses = {}; - GetContext().clauseInfo = {}; - } - void SetContextDirectiveSource(const parser::CharBlock &directive) { - GetContext().directiveSource = directive; - } - void SetContextClause(const parser::OmpClause &clause) { - GetContext().clauseSource = clause.source; - GetContext().clause = &clause; - } - void SetContextDirectiveEnum(llvm::omp::Directive dir) { - GetContext().directive = dir; - } - void SetContextAllowed(const OmpClauseSet &allowed) { - GetContext().allowedClauses = allowed; - } - void SetContextAllowedOnce(const OmpClauseSet &allowedOnce) { - GetContext().allowedOnceClauses = allowedOnce; - } - void SetContextAllowedExclusive(const OmpClauseSet &allowedExclusive) { - GetContext().allowedExclusiveClauses = allowedExclusive; - } - void SetContextRequired(const OmpClauseSet &required) { - GetContext().requiredClauses = required; - } - void SetContextClauseInfo(llvm::omp::Clause type) { - GetContext().clauseInfo.emplace(type, GetContext().clause); - } - const parser::OmpClause *FindClause(llvm::omp::Clause type) { - auto it{GetContext().clauseInfo.find(type)}; - if (it != GetContext().clauseInfo.end()) { - return it->second; - } - return nullptr; - } - void PushContext(const parser::CharBlock &source, llvm::omp::Directive dir) { - ompContext_.emplace_back(source, dir); - } - void SetClauseSets(llvm::omp::Directive dir) { - ompContext_.back().allowedClauses = directiveClausesTable[dir].allowed; - ompContext_.back().allowedOnceClauses = - directiveClausesTable[dir].allowedOnce; - ompContext_.back().allowedExclusiveClauses = - directiveClausesTable[dir].allowedExclusive; - ompContext_.back().requiredClauses = - directiveClausesTable[dir].requiredOneOf; - } - void PushContextAndClauseSets( - const parser::CharBlock &source, llvm::omp::Directive dir) { - PushContext(source, dir); - SetClauseSets(dir); - } - void RequiresConstantPositiveParameter( - const llvm::omp::Clause &clause, const parser::ScalarIntConstantExpr &i); - void RequiresPositiveParameter( - const llvm::omp::Clause &clause, const parser::ScalarIntExpr &i); - - bool CurrentDirectiveIsNested() { return ompContext_.size() > 0; }; bool HasInvalidWorksharingNesting( const parser::CharBlock &, const OmpDirectiveSet &); - void CheckAllowed(llvm::omp::Clause); - void CheckRequired(llvm::omp::Clause); - std::string ContextDirectiveAsFortran(); - void SayNotMatching(const parser::CharBlock &, const parser::CharBlock &); - template - const A &CheckMatching(const B &beginDir, const C &endDir) { - const A &begin{std::get(beginDir.t)}; - const A &end{std::get(endDir.t)}; - if (begin.v != end.v) { - SayNotMatching(begin.source, end.source); - } - return begin; - } // specific clause related bool ScheduleModifierHasType(const parser::OmpScheduleClause &, const parser::OmpScheduleModifierType::ModType &); - SemanticsContext &context_; - std::vector ompContext_; // used as a stack + llvm::StringRef getClauseName(llvm::omp::Clause clause) override; + llvm::StringRef getDirectiveName(llvm::omp::Directive directive) override; }; } // namespace Fortran::semantics #endif // FORTRAN_SEMANTICS_CHECK_OMP_STRUCTURE_H_ diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp --- a/flang/lib/Semantics/check-omp-structure.cpp +++ b/flang/lib/Semantics/check-omp-structure.cpp @@ -12,21 +12,6 @@ namespace Fortran::semantics { -std::string OmpStructureChecker::ContextDirectiveAsFortran() { - auto dir = llvm::omp::getOpenMPDirectiveName(GetContext().directive).str(); - std::transform(dir.begin(), dir.end(), dir.begin(), - [](unsigned char c) { return std::toupper(c); }); - return dir; -} - -void OmpStructureChecker::SayNotMatching( - const parser::CharBlock &beginSource, const parser::CharBlock &endSource) { - context_ - .Say(endSource, "Unmatched %s directive"_err_en_US, - parser::ToUpperCaseLetters(endSource.ToString())) - .Attach(beginSource, "Does not match directive"_en_US); -} - bool OmpStructureChecker::HasInvalidWorksharingNesting( const parser::CharBlock &source, const OmpDirectiveSet &set) { // set contains all the invalid closely nested directives @@ -41,85 +26,6 @@ return false; } -void OmpStructureChecker::CheckAllowed(llvm::omp::Clause type) { - if (!GetContext().allowedClauses.test(type) && - !GetContext().allowedOnceClauses.test(type) && - !GetContext().allowedExclusiveClauses.test(type) && - !GetContext().requiredClauses.test(type)) { - context_.Say(GetContext().clauseSource, - "%s clause is not allowed on the %s directive"_err_en_US, - parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(type).str()), - parser::ToUpperCaseLetters(GetContext().directiveSource.ToString())); - return; - } - if ((GetContext().allowedOnceClauses.test(type) || - GetContext().allowedExclusiveClauses.test(type)) && - FindClause(type)) { - context_.Say(GetContext().clauseSource, - "At most one %s clause can appear on the %s directive"_err_en_US, - parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(type).str()), - parser::ToUpperCaseLetters(GetContext().directiveSource.ToString())); - return; - } - if (GetContext().allowedExclusiveClauses.test(type)) { - std::vector others; - GetContext().allowedExclusiveClauses.IterateOverMembers( - [&](llvm::omp::Clause o) { - if (FindClause(o)) { - others.emplace_back(o); - } - }); - for (const auto &e : others) { - context_.Say(GetContext().clauseSource, - "%s and %s are mutually exclusive and may not appear on the " - "same %s directive"_err_en_US, - parser::ToUpperCaseLetters( - llvm::omp::getOpenMPClauseName(type).str()), - parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(e).str()), - parser::ToUpperCaseLetters(GetContext().directiveSource.ToString())); - } - if (!others.empty()) { - return; - } - } - SetContextClauseInfo(type); -} - -void OmpStructureChecker::CheckRequired(llvm::omp::Clause c) { - if (!FindClause(c)) { - context_.Say(GetContext().directiveSource, - "At least one %s clause must appear on the %s directive"_err_en_US, - parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(c).str()), - ContextDirectiveAsFortran()); - } -} - -void OmpStructureChecker::RequiresConstantPositiveParameter( - const llvm::omp::Clause &clause, const parser::ScalarIntConstantExpr &i) { - if (const auto v{GetIntValue(i)}) { - if (*v <= 0) { - context_.Say(GetContext().clauseSource, - "The parameter of the %s clause must be " - "a constant positive integer expression"_err_en_US, - parser::ToUpperCaseLetters( - llvm::omp::getOpenMPClauseName(clause).str())); - } - } -} - -void OmpStructureChecker::RequiresPositiveParameter( - const llvm::omp::Clause &clause, const parser::ScalarIntExpr &i) { - if (const auto v{GetIntValue(i)}) { - if (*v <= 0) { - context_.Say(GetContext().clauseSource, - "The parameter of the %s clause must be " - "a positive integer expression"_err_en_US, - parser::ToUpperCaseLetters( - llvm::omp::getOpenMPClauseName(clause).str())); - } - } -} - void OmpStructureChecker::Enter(const parser::OpenMPConstruct &) { // 2.8.1 TODO: Simd Construct with Ordered Construct Nesting check } @@ -131,7 +37,10 @@ // check matching, End directive is optional if (const auto &endLoopDir{ std::get>(x.t)}) { - CheckMatching(beginLoopDir, *endLoopDir); + const auto &endDir{ + std::get(endLoopDir.value().t)}; + + CheckMatching(beginDir, endDir); } if (beginDir.v != llvm::omp::Directive::OMPD_do) { @@ -162,7 +71,7 @@ } void OmpStructureChecker::Leave(const parser::OpenMPLoopConstruct &) { - ompContext_.pop_back(); + dirContext_.pop_back(); } void OmpStructureChecker::Enter(const parser::OmpEndLoopDirective &x) { @@ -184,28 +93,31 @@ void OmpStructureChecker::Enter(const parser::OpenMPBlockConstruct &x) { const auto &beginBlockDir{std::get(x.t)}; const auto &endBlockDir{std::get(x.t)}; - const auto &beginDir{ - CheckMatching(beginBlockDir, endBlockDir)}; + const auto &beginDir{std::get(beginBlockDir.t)}; + const auto &endDir{std::get(endBlockDir.t)}; + CheckMatching(beginDir, endDir); PushContextAndClauseSets(beginDir.source, beginDir.v); } void OmpStructureChecker::Leave(const parser::OpenMPBlockConstruct &) { - ompContext_.pop_back(); + dirContext_.pop_back(); } void OmpStructureChecker::Enter(const parser::OpenMPSectionsConstruct &x) { const auto &beginSectionsDir{ std::get(x.t)}; const auto &endSectionsDir{std::get(x.t)}; - const auto &beginDir{CheckMatching( - beginSectionsDir, endSectionsDir)}; + const auto &beginDir{ + std::get(beginSectionsDir.t)}; + const auto &endDir{std::get(endSectionsDir.t)}; + CheckMatching(beginDir, endDir); PushContextAndClauseSets(beginDir.source, beginDir.v); } void OmpStructureChecker::Leave(const parser::OpenMPSectionsConstruct &) { - ompContext_.pop_back(); + dirContext_.pop_back(); } void OmpStructureChecker::Enter(const parser::OmpEndSectionsDirective &x) { @@ -229,7 +141,7 @@ } void OmpStructureChecker::Leave(const parser::OpenMPDeclareSimdConstruct &) { - ompContext_.pop_back(); + dirContext_.pop_back(); } void OmpStructureChecker::Enter(const parser::OpenMPDeclareTargetConstruct &x) { @@ -243,7 +155,7 @@ } void OmpStructureChecker::Leave(const parser::OpenMPDeclareTargetConstruct &) { - ompContext_.pop_back(); + dirContext_.pop_back(); } void OmpStructureChecker::Enter( @@ -254,7 +166,7 @@ void OmpStructureChecker::Leave( const parser::OpenMPSimpleStandaloneConstruct &) { - ompContext_.pop_back(); + dirContext_.pop_back(); } void OmpStructureChecker::Enter(const parser::OpenMPFlushConstruct &x) { @@ -263,7 +175,7 @@ } void OmpStructureChecker::Leave(const parser::OpenMPFlushConstruct &) { - ompContext_.pop_back(); + dirContext_.pop_back(); } void OmpStructureChecker::Enter(const parser::OpenMPCancelConstruct &x) { @@ -272,7 +184,7 @@ } void OmpStructureChecker::Leave(const parser::OpenMPCancelConstruct &) { - ompContext_.pop_back(); + dirContext_.pop_back(); } void OmpStructureChecker::Enter( @@ -284,7 +196,7 @@ void OmpStructureChecker::Leave( const parser::OpenMPCancellationPointConstruct &) { - ompContext_.pop_back(); + dirContext_.pop_back(); } void OmpStructureChecker::Enter(const parser::OmpEndBlockDirective &x) { @@ -701,4 +613,14 @@ } } } + +llvm::StringRef OmpStructureChecker::getClauseName(llvm::omp::Clause clause) { + return llvm::omp::getOpenMPClauseName(clause); +} + +llvm::StringRef OmpStructureChecker::getDirectiveName( + llvm::omp::Directive directive) { + return llvm::omp::getOpenMPDirectiveName(directive); +} + } // namespace Fortran::semantics diff --git a/flang/test/Semantics/acc-clause-validity.f90 b/flang/test/Semantics/acc-clause-validity.f90 --- a/flang/test/Semantics/acc-clause-validity.f90 +++ b/flang/test/Semantics/acc-clause-validity.f90 @@ -82,7 +82,7 @@ !$acc end parallel !$acc parallel - !ERROR: The parameter of the COLLAPSE clause on the LOOP directive must be a constant positive integer expression + !ERROR: The parameter of the COLLAPSE clause must be a constant positive integer expression !$acc loop collapse(-1) do i = 1, N do j = 1, N diff --git a/flang/test/Semantics/omp-clause-validity01.f90 b/flang/test/Semantics/omp-clause-validity01.f90 --- a/flang/test/Semantics/omp-clause-validity01.f90 +++ b/flang/test/Semantics/omp-clause-validity01.f90 @@ -364,7 +364,7 @@ a = 3.14 enddo - !ERROR: GRAINSIZE and NUM_TASKS are mutually exclusive and may not appear on the same TASKLOOP directive + !ERROR: GRAINSIZE and NUM_TASKS clauses are mutually exclusive and may not appear on the same TASKLOOP directive !$omp taskloop num_tasks(3) grainsize(2) do i = 1,N a = 3.14 @@ -468,7 +468,7 @@ !ERROR: Unmatched END TASKLOOP directive !$omp end taskloop - !ERROR: GRAINSIZE and NUM_TASKS are mutually exclusive and may not appear on the same TASKLOOP SIMD directive + !ERROR: GRAINSIZE and NUM_TASKS clauses are mutually exclusive and may not appear on the same TASKLOOP SIMD directive !$omp taskloop simd num_tasks(3) grainsize(2) do i = 1,N a = 3.14 diff --git a/flang/test/Semantics/omp-declarative-directive.f90 b/flang/test/Semantics/omp-declarative-directive.f90 --- a/flang/test/Semantics/omp-declarative-directive.f90 +++ b/flang/test/Semantics/omp-declarative-directive.f90 @@ -28,7 +28,7 @@ use m1 procedure (sub) sub1 !ERROR: Internal: no symbol found for 'sub1' - !ERROR: NOTINBRANCH and INBRANCH are mutually exclusive and may not appear on the same DECLARE SIMD directive + !ERROR: NOTINBRANCH and INBRANCH clauses are mutually exclusive and may not appear on the same DECLARE SIMD directive !$omp declare simd(sub1) inbranch notinbranch procedure (sub), pointer::p p=>sub1 diff --git a/llvm/test/TableGen/directive1.td b/llvm/test/TableGen/directive1.td --- a/llvm/test/TableGen/directive1.td +++ b/llvm/test/TableGen/directive1.td @@ -161,15 +161,7 @@ // GEN-NEXT: #ifdef GEN_FLANG_DIRECTIVE_CLAUSE_MAP // GEN-NEXT: #undef GEN_FLANG_DIRECTIVE_CLAUSE_MAP // GEN-EMPTY: -// GEN-NEXT: struct TdlDirectiveClauses { -// GEN-NEXT: const allowed; -// GEN-NEXT: const allowedOnce; -// GEN-NEXT: const allowedExclusive; -// GEN-NEXT: const requiredOneOf; -// GEN-NEXT: }; -// GEN-EMPTY: -// GEN-NEXT: std::unordered_map -// GEN-NEXT: directiveClausesTable = { +// GEN-NEXT: { // GEN-NEXT: {llvm::tdl::Directive::TDLD_dira, // GEN-NEXT: { // GEN-NEXT: llvm::tdl::allowedClauses_TDLD_dira, @@ -178,7 +170,7 @@ // GEN-NEXT: llvm::tdl::requiredClauses_TDLD_dira, // GEN-NEXT: } // GEN-NEXT: }, -// GEN-NEXT: }; +// GEN-NEXT: } // GEN-EMPTY: // GEN-NEXT: #endif // GEN_FLANG_DIRECTIVE_CLAUSE_MAP diff --git a/llvm/test/TableGen/directive2.td b/llvm/test/TableGen/directive2.td --- a/llvm/test/TableGen/directive2.td +++ b/llvm/test/TableGen/directive2.td @@ -152,15 +152,7 @@ // GEN-NEXT: #ifdef GEN_FLANG_DIRECTIVE_CLAUSE_MAP // GEN-NEXT: #undef GEN_FLANG_DIRECTIVE_CLAUSE_MAP // GEN-EMPTY: -// GEN-NEXT: struct TdlDirectiveClauses { -// GEN-NEXT: const allowed; -// GEN-NEXT: const allowedOnce; -// GEN-NEXT: const allowedExclusive; -// GEN-NEXT: const requiredOneOf; -// GEN-NEXT: }; -// GEN-EMPTY: -// GEN-NEXT: std::unordered_map -// GEN-NEXT: directiveClausesTable = { +// GEN-NEXT: { // GEN-NEXT: {llvm::tdl::Directive::TDLD_dira, // GEN-NEXT: { // GEN-NEXT: llvm::tdl::allowedClauses_TDLD_dira, @@ -169,6 +161,6 @@ // GEN-NEXT: llvm::tdl::requiredClauses_TDLD_dira, // GEN-NEXT: } // GEN-NEXT: }, -// GEN-NEXT: }; +// GEN-NEXT: } // GEN-EMPTY: // GEN-NEXT: #endif // GEN_FLANG_DIRECTIVE_CLAUSE_MAP diff --git a/llvm/utils/TableGen/DirectiveEmitter.cpp b/llvm/utils/TableGen/DirectiveEmitter.cpp --- a/llvm/utils/TableGen/DirectiveEmitter.cpp +++ b/llvm/utils/TableGen/DirectiveEmitter.cpp @@ -465,18 +465,7 @@ IfDefScope Scope("GEN_FLANG_DIRECTIVE_CLAUSE_MAP", OS); OS << "\n"; - OS << "struct " << DirLang.getName() << "DirectiveClauses {\n"; - OS << " const " << DirLang.getClauseEnumSetClass() << " allowed;\n"; - OS << " const " << DirLang.getClauseEnumSetClass() << " allowedOnce;\n"; - OS << " const " << DirLang.getClauseEnumSetClass() << " allowedExclusive;\n"; - OS << " const " << DirLang.getClauseEnumSetClass() << " requiredOneOf;\n"; - OS << "};\n"; - - OS << "\n"; - - OS << "std::unordered_map\n"; - OS << " directiveClausesTable = {\n"; + OS << "{\n"; for (const auto &D : Directives) { Directive Dir{D}; @@ -497,7 +486,7 @@ OS << " },\n"; } - OS << "};\n"; + OS << "}\n"; } // Generate the implemenation section for the enumeration in the directive