[flang][OpenMP] Semantic checks for context selectors (#123243)

This implements checks of the validity of context set selectors and
trait selectors, plus the types of trait properties. Clause properties
are also validated, but not name or extension properties.

---------

Co-authored-by: Tom Eccles <tom.eccles@arm.com>
This commit is contained in:
Krzysztof Parzyszek
2025-02-03 09:48:04 -06:00
committed by GitHub
parent 0e49c74f36
commit fe8b323f59
12 changed files with 743 additions and 9 deletions

View File

@@ -3572,6 +3572,7 @@ struct OmpTraitProperty {
// Trait-set-selectors:
// [D]evice, [T]arget_device, [C]onstruct, [I]mplementation, [U]ser.
struct OmpTraitSelectorName {
std::string ToString() const;
CharBlock source;
UNION_CLASS_BOILERPLATE(OmpTraitSelectorName);
ENUM_CLASS(Value, Arch, Atomic_Default_Mem_Order, Condition, Device_Num,
@@ -3596,6 +3597,7 @@ struct OmpTraitSelector {
// CONSTRUCT | DEVICE | IMPLEMENTATION | USER | // since 5.0
// TARGET_DEVICE // since 5.1
struct OmpTraitSetSelectorName {
std::string ToString() const;
CharBlock source;
ENUM_CLASS(Value, Construct, Device, Implementation, Target_Device, User)
WRAPPER_CLASS_BOILERPLATE(OmpTraitSetSelectorName, Value);

View File

@@ -72,6 +72,7 @@ DECLARE_DESCRIPTOR(parser::OmpAlignModifier);
DECLARE_DESCRIPTOR(parser::OmpAllocatorComplexModifier);
DECLARE_DESCRIPTOR(parser::OmpAllocatorSimpleModifier);
DECLARE_DESCRIPTOR(parser::OmpChunkModifier);
DECLARE_DESCRIPTOR(parser::OmpContextSelector);
DECLARE_DESCRIPTOR(parser::OmpDependenceType);
DECLARE_DESCRIPTOR(parser::OmpDeviceModifier);
DECLARE_DESCRIPTOR(parser::OmpDirectiveNameModifier);

View File

@@ -281,6 +281,26 @@ OmpTaskDependenceType::Value OmpDependClause::TaskDep::GetTaskDepType() const {
}
}
std::string OmpTraitSelectorName::ToString() const {
return common::visit( //
common::visitors{
[&](Value v) { //
return std::string(EnumToString(v));
},
[&](llvm::omp::Directive d) {
return llvm::omp::getOpenMPDirectiveName(d).str();
},
[&](const std::string &s) { //
return s;
},
},
u);
}
std::string OmpTraitSetSelectorName::ToString() const {
return std::string(EnumToString(v));
}
} // namespace Fortran::parser
template <typename C> static llvm::omp::Clause getClauseIdForClass(C &&) {

View File

@@ -9,6 +9,8 @@
#include "check-omp-structure.h"
#include "definable.h"
#include "flang/Evaluate/check-expression.h"
#include "flang/Evaluate/expression.h"
#include "flang/Evaluate/type.h"
#include "flang/Parser/parse-tree.h"
#include "flang/Semantics/expression.h"
#include "flang/Semantics/openmp-modifiers.h"
@@ -2985,7 +2987,6 @@ CHECK_SIMPLE_CLAUSE(Severity, OMPC_severity)
CHECK_SIMPLE_CLAUSE(Message, OMPC_message)
CHECK_SIMPLE_CLAUSE(Filter, OMPC_filter)
CHECK_SIMPLE_CLAUSE(Otherwise, OMPC_otherwise)
CHECK_SIMPLE_CLAUSE(When, OMPC_when)
CHECK_SIMPLE_CLAUSE(AdjustArgs, OMPC_adjust_args)
CHECK_SIMPLE_CLAUSE(AppendArgs, OMPC_append_args)
CHECK_SIMPLE_CLAUSE(MemoryOrder, OMPC_memory_order)
@@ -4537,14 +4538,513 @@ void OmpStructureChecker::Enter(const parser::OmpClause::OmpxBare &x) {
}
}
void OmpStructureChecker::Enter(const parser::OmpContextSelector &ctxSel) {
void OmpStructureChecker::Enter(const parser::OmpClause::When &x) {
CheckAllowedClause(llvm::omp::Clause::OMPC_when);
OmpVerifyModifiers(
x.v, llvm::omp::OMPC_when, GetContext().clauseSource, context_);
}
void OmpStructureChecker::Enter(const parser::OmpContextSelector &ctx) {
EnterDirectiveNest(ContextSelectorNest);
using SetName = parser::OmpTraitSetSelectorName;
std::map<SetName::Value, const SetName *> visited;
for (const parser::OmpTraitSetSelector &traitSet : ctx.v) {
auto &name{std::get<SetName>(traitSet.t)};
auto [prev, unique]{visited.insert(std::make_pair(name.v, &name))};
if (!unique) {
std::string showName{parser::ToUpperCaseLetters(name.ToString())};
parser::MessageFormattedText txt(
"Repeated trait set name %s in a context specifier"_err_en_US,
showName);
parser::Message message(name.source, txt);
message.Attach(prev->second->source,
"Previous trait set %s provided here"_en_US, showName);
context_.Say(std::move(message));
}
CheckTraitSetSelector(traitSet);
}
}
void OmpStructureChecker::Leave(const parser::OmpContextSelector &) {
ExitDirectiveNest(ContextSelectorNest);
}
std::optional<evaluate::DynamicType> OmpStructureChecker::GetDynamicType(
const common::Indirection<parser::Expr> &parserExpr) {
// Indirection<parser::Expr> parserExpr
// `- parser::Expr ^.value()
const parser::TypedExpr &typedExpr{parserExpr.value().typedExpr};
// ForwardOwningPointer typedExpr
// `- GenericExprWrapper ^.get()
// `- std::optional<Expr> ^->v
if (auto maybeExpr{typedExpr.get()->v}) {
return maybeExpr->GetType();
} else {
return std::nullopt;
}
}
const std::list<parser::OmpTraitProperty> &
OmpStructureChecker::GetTraitPropertyList(
const parser::OmpTraitSelector &trait) {
static const std::list<parser::OmpTraitProperty> empty{};
auto &[_, maybeProps]{trait.t};
if (maybeProps) {
using PropertyList = std::list<parser::OmpTraitProperty>;
return std::get<PropertyList>(maybeProps->t);
} else {
return empty;
}
}
std::optional<llvm::omp::Clause> OmpStructureChecker::GetClauseFromProperty(
const parser::OmpTraitProperty &property) {
using MaybeClause = std::optional<llvm::omp::Clause>;
// The parser for OmpClause will only succeed if the clause was
// given with all required arguments.
// If this is a string or complex extension with a clause name,
// treat it as a clause and let the trait checker deal with it.
auto getClauseFromString{[&](const std::string &s) -> MaybeClause {
auto id{llvm::omp::getOpenMPClauseKind(parser::ToLowerCaseLetters(s))};
if (id != llvm::omp::Clause::OMPC_unknown) {
return id;
} else {
return std::nullopt;
}
}};
return common::visit( //
common::visitors{
[&](const parser::OmpTraitPropertyName &x) -> MaybeClause {
return getClauseFromString(x.v);
},
[&](const common::Indirection<parser::OmpClause> &x) -> MaybeClause {
return x.value().Id();
},
[&](const parser::ScalarExpr &x) -> MaybeClause {
return std::nullopt;
},
[&](const parser::OmpTraitPropertyExtension &x) -> MaybeClause {
using ExtProperty = parser::OmpTraitPropertyExtension;
if (auto *name{std::get_if<parser::OmpTraitPropertyName>(&x.u)}) {
return getClauseFromString(name->v);
} else if (auto *cpx{std::get_if<ExtProperty::Complex>(&x.u)}) {
return getClauseFromString(
std::get<parser::OmpTraitPropertyName>(cpx->t).v);
}
return std::nullopt;
},
},
property.u);
}
void OmpStructureChecker::CheckTraitSelectorList(
const std::list<parser::OmpTraitSelector> &traits) {
// [6.0:322:20]
// Each trait-selector-name may only be specified once in a trait selector
// set.
// Cannot store OmpTraitSelectorName directly, because it's not copyable.
using TraitName = parser::OmpTraitSelectorName;
using BareName = decltype(TraitName::u);
std::map<BareName, const TraitName *> visited;
for (const parser::OmpTraitSelector &trait : traits) {
auto &name{std::get<TraitName>(trait.t)};
auto [prev, unique]{visited.insert(std::make_pair(name.u, &name))};
if (!unique) {
std::string showName{parser::ToUpperCaseLetters(name.ToString())};
parser::MessageFormattedText txt(
"Repeated trait name %s in a trait set"_err_en_US, showName);
parser::Message message(name.source, txt);
message.Attach(prev->second->source,
"Previous trait %s provided here"_en_US, showName);
context_.Say(std::move(message));
}
}
}
void OmpStructureChecker::CheckTraitSetSelector(
const parser::OmpTraitSetSelector &traitSet) {
// Trait Set | Allowed traits | D-traits | X-traits | Score |
//
// Construct | Simd, directive-name | Yes | No | No |
// Device | Arch, Isa, Kind | No | Yes | No |
// Implementation | Atomic_Default_Mem_Order | No | Yes | Yes |
// | Extension, Requires | | | |
// | Vendor | | | |
// Target_Device | Arch, Device_Num, Isa | No | Yes | No |
// | Kind, Uid | | | |
// User | Condition | No | No | Yes |
struct TraitSetConfig {
std::set<parser::OmpTraitSelectorName::Value> allowed;
bool allowsDirectiveTraits;
bool allowsExtensionTraits;
bool allowsScore;
};
using SName = parser::OmpTraitSetSelectorName::Value;
using TName = parser::OmpTraitSelectorName::Value;
static const std::map<SName, TraitSetConfig> configs{
{SName::Construct, //
{{TName::Simd}, true, false, false}},
{SName::Device, //
{{TName::Arch, TName::Isa, TName::Kind}, false, true, false}},
{SName::Implementation, //
{{TName::Atomic_Default_Mem_Order, TName::Extension, TName::Requires,
TName::Vendor},
false, true, true}},
{SName::Target_Device, //
{{TName::Arch, TName::Device_Num, TName::Isa, TName::Kind,
TName::Uid},
false, true, false}},
{SName::User, //
{{TName::Condition}, false, false, true}},
};
auto checkTraitSet{[&](const TraitSetConfig &config) {
auto &[setName, traits]{traitSet.t};
auto usn{parser::ToUpperCaseLetters(setName.ToString())};
// Check if there are any duplicate traits.
CheckTraitSelectorList(traits);
for (const parser::OmpTraitSelector &trait : traits) {
auto &[traitName, maybeProps]{trait.t};
// Check allowed traits
common::visit( //
common::visitors{
[&](parser::OmpTraitSelectorName::Value v) {
if (!config.allowed.count(v)) {
context_.Say(traitName.source,
"%s is not a valid trait for %s trait set"_err_en_US,
parser::ToUpperCaseLetters(traitName.ToString()), usn);
}
},
[&](llvm::omp::Directive) {
if (!config.allowsDirectiveTraits) {
context_.Say(traitName.source,
"Directive name is not a valid trait for %s trait set"_err_en_US,
usn);
}
},
[&](const std::string &) {
if (!config.allowsExtensionTraits) {
context_.Say(traitName.source,
"Extension traits are not valid for %s trait set"_err_en_US,
usn);
}
},
},
traitName.u);
// Check score
if (maybeProps) {
auto &[maybeScore, _]{maybeProps->t};
if (maybeScore) {
CheckTraitScore(*maybeScore);
}
}
// Check the properties of the individual traits
CheckTraitSelector(traitSet, trait);
}
}};
checkTraitSet(
configs.at(std::get<parser::OmpTraitSetSelectorName>(traitSet.t).v));
}
void OmpStructureChecker::CheckTraitScore(const parser::OmpTraitScore &score) {
// [6.0:322:23]
// A score-expression must be a non-negative constant integer expression.
if (auto value{GetIntValue(score)}; !value || value <= 0) {
context_.Say(score.source,
"SCORE expression must be a non-negative constant integer expression"_err_en_US);
}
}
bool OmpStructureChecker::VerifyTraitPropertyLists(
const parser::OmpTraitSetSelector &traitSet,
const parser::OmpTraitSelector &trait) {
using TraitName = parser::OmpTraitSelectorName;
using PropertyList = std::list<parser::OmpTraitProperty>;
auto &[traitName, maybeProps]{trait.t};
auto checkPropertyList{[&](const PropertyList &properties, auto isValid,
const std::string &message) {
bool foundInvalid{false};
for (const parser::OmpTraitProperty &prop : properties) {
if (!isValid(prop)) {
if (foundInvalid) {
context_.Say(
prop.source, "More invalid properties are present"_err_en_US);
break;
}
context_.Say(prop.source, "%s"_err_en_US, message);
foundInvalid = true;
}
}
return !foundInvalid;
}};
bool invalid{false};
if (std::holds_alternative<llvm::omp::Directive>(traitName.u)) {
// Directive-name traits don't have properties.
if (maybeProps) {
context_.Say(trait.source,
"Directive-name traits cannot have properties"_err_en_US);
invalid = true;
}
}
// Ignore properties on extension traits.
// See `TraitSelectorParser` in openmp-parser.cpp
if (auto *v{std::get_if<TraitName::Value>(&traitName.u)}) {
switch (*v) {
// name-list properties
case parser::OmpTraitSelectorName::Value::Arch:
case parser::OmpTraitSelectorName::Value::Extension:
case parser::OmpTraitSelectorName::Value::Isa:
case parser::OmpTraitSelectorName::Value::Kind:
case parser::OmpTraitSelectorName::Value::Uid:
case parser::OmpTraitSelectorName::Value::Vendor:
if (maybeProps) {
auto isName{[](const parser::OmpTraitProperty &prop) {
return std::holds_alternative<parser::OmpTraitPropertyName>(prop.u);
}};
invalid = !checkPropertyList(std::get<PropertyList>(maybeProps->t),
isName, "Trait property should be a name");
}
break;
// clause-list
case parser::OmpTraitSelectorName::Value::Atomic_Default_Mem_Order:
case parser::OmpTraitSelectorName::Value::Requires:
case parser::OmpTraitSelectorName::Value::Simd:
if (maybeProps) {
auto isClause{[&](const parser::OmpTraitProperty &prop) {
return GetClauseFromProperty(prop).has_value();
}};
invalid = !checkPropertyList(std::get<PropertyList>(maybeProps->t),
isClause, "Trait property should be a clause");
}
break;
// expr-list
case parser::OmpTraitSelectorName::Value::Condition:
case parser::OmpTraitSelectorName::Value::Device_Num:
if (maybeProps) {
auto isExpr{[](const parser::OmpTraitProperty &prop) {
return std::holds_alternative<parser::ScalarExpr>(prop.u);
}};
invalid = !checkPropertyList(std::get<PropertyList>(maybeProps->t),
isExpr, "Trait property should be a scalar expression");
}
break;
} // switch
}
return !invalid;
}
void OmpStructureChecker::CheckTraitSelector(
const parser::OmpTraitSetSelector &traitSet,
const parser::OmpTraitSelector &trait) {
using TraitName = parser::OmpTraitSelectorName;
auto &[traitName, maybeProps]{trait.t};
// Only do the detailed checks if the property lists are valid.
if (VerifyTraitPropertyLists(traitSet, trait)) {
if (std::holds_alternative<llvm::omp::Directive>(traitName.u) ||
std::holds_alternative<std::string>(traitName.u)) {
// No properties here: directives don't have properties, and
// we don't implement any extension traits now.
return;
}
// Specific traits we want to check.
// Limitations:
// (1) The properties for these traits are defined in "Additional
// Definitions for the OpenMP API Specification". It's not clear how
// to define them in a portable way, and how to verify their validity,
// especially if they get replaced by their integer values (in case
// they are defined as enums).
// (2) These are entirely implementation-defined, and at the moment
// there is no known schema to validate these values.
auto v{std::get<TraitName::Value>(traitName.u)};
switch (v) {
case TraitName::Value::Arch:
// Unchecked, TBD(1)
break;
case TraitName::Value::Atomic_Default_Mem_Order:
CheckTraitADMO(traitSet, trait);
break;
case TraitName::Value::Condition:
CheckTraitCondition(traitSet, trait);
break;
case TraitName::Value::Device_Num:
CheckTraitDeviceNum(traitSet, trait);
break;
case TraitName::Value::Extension:
// Ignore
break;
case TraitName::Value::Isa:
// Unchecked, TBD(1)
break;
case TraitName::Value::Kind:
// Unchecked, TBD(1)
break;
case TraitName::Value::Requires:
CheckTraitRequires(traitSet, trait);
break;
case TraitName::Value::Simd:
CheckTraitSimd(traitSet, trait);
break;
case TraitName::Value::Uid:
// Unchecked, TBD(2)
break;
case TraitName::Value::Vendor:
// Unchecked, TBD(1)
break;
}
}
}
void OmpStructureChecker::CheckTraitADMO(
const parser::OmpTraitSetSelector &traitSet,
const parser::OmpTraitSelector &trait) {
auto &traitName{std::get<parser::OmpTraitSelectorName>(trait.t)};
auto &properties{GetTraitPropertyList(trait)};
if (properties.size() != 1) {
context_.Say(trait.source,
"%s trait requires a single clause property"_err_en_US,
parser::ToUpperCaseLetters(traitName.ToString()));
} else {
const parser::OmpTraitProperty &property{properties.front()};
auto clauseId{*GetClauseFromProperty(property)};
// Check that the clause belongs to the memory-order clause-set.
// Clause sets will hopefully be autogenerated at some point.
switch (clauseId) {
case llvm::omp::Clause::OMPC_acq_rel:
case llvm::omp::Clause::OMPC_acquire:
case llvm::omp::Clause::OMPC_relaxed:
case llvm::omp::Clause::OMPC_release:
case llvm::omp::Clause::OMPC_seq_cst:
break;
default:
context_.Say(property.source,
"%s trait requires a clause from the memory-order clause set"_err_en_US,
parser::ToUpperCaseLetters(traitName.ToString()));
}
using ClauseProperty = common::Indirection<parser::OmpClause>;
if (!std::holds_alternative<ClauseProperty>(property.u)) {
context_.Say(property.source,
"Invalid clause specification for %s"_err_en_US,
parser::ToUpperCaseLetters(getClauseName(clauseId)));
}
}
}
void OmpStructureChecker::CheckTraitCondition(
const parser::OmpTraitSetSelector &traitSet,
const parser::OmpTraitSelector &trait) {
auto &traitName{std::get<parser::OmpTraitSelectorName>(trait.t)};
auto &properties{GetTraitPropertyList(trait)};
if (properties.size() != 1) {
context_.Say(trait.source,
"%s trait requires a single expression property"_err_en_US,
parser::ToUpperCaseLetters(traitName.ToString()));
} else {
const parser::OmpTraitProperty &property{properties.front()};
auto &scalarExpr{std::get<parser::ScalarExpr>(property.u)};
auto maybeType{GetDynamicType(scalarExpr.thing)};
if (!maybeType || maybeType->category() != TypeCategory::Logical) {
context_.Say(property.source,
"%s trait requires a single LOGICAL expression"_err_en_US,
parser::ToUpperCaseLetters(traitName.ToString()));
}
}
}
void OmpStructureChecker::CheckTraitDeviceNum(
const parser::OmpTraitSetSelector &traitSet,
const parser::OmpTraitSelector &trait) {
auto &traitName{std::get<parser::OmpTraitSelectorName>(trait.t)};
auto &properties{GetTraitPropertyList(trait)};
if (properties.size() != 1) {
context_.Say(trait.source,
"%s trait requires a single expression property"_err_en_US,
parser::ToUpperCaseLetters(traitName.ToString()));
}
// No other checks at the moment.
}
void OmpStructureChecker::CheckTraitRequires(
const parser::OmpTraitSetSelector &traitSet,
const parser::OmpTraitSelector &trait) {
unsigned version{context_.langOptions().OpenMPVersion};
auto &traitName{std::get<parser::OmpTraitSelectorName>(trait.t)};
auto &properties{GetTraitPropertyList(trait)};
for (const parser::OmpTraitProperty &property : properties) {
auto clauseId{*GetClauseFromProperty(property)};
if (!llvm::omp::isAllowedClauseForDirective(
llvm::omp::OMPD_requires, clauseId, version)) {
context_.Say(property.source,
"%s trait requires a clause from the requirement clause set"_err_en_US,
parser::ToUpperCaseLetters(traitName.ToString()));
}
using ClauseProperty = common::Indirection<parser::OmpClause>;
if (!std::holds_alternative<ClauseProperty>(property.u)) {
context_.Say(property.source,
"Invalid clause specification for %s"_err_en_US,
parser::ToUpperCaseLetters(getClauseName(clauseId)));
}
}
}
void OmpStructureChecker::CheckTraitSimd(
const parser::OmpTraitSetSelector &traitSet,
const parser::OmpTraitSelector &trait) {
unsigned version{context_.langOptions().OpenMPVersion};
auto &traitName{std::get<parser::OmpTraitSelectorName>(trait.t)};
auto &properties{GetTraitPropertyList(trait)};
for (const parser::OmpTraitProperty &property : properties) {
auto clauseId{*GetClauseFromProperty(property)};
if (!llvm::omp::isAllowedClauseForDirective(
llvm::omp::OMPD_declare_simd, clauseId, version)) {
context_.Say(property.source,
"%s trait requires a clause that is allowed on the %s directive"_err_en_US,
parser::ToUpperCaseLetters(traitName.ToString()),
parser::ToUpperCaseLetters(
getDirectiveName(llvm::omp::OMPD_declare_simd)));
}
using ClauseProperty = common::Indirection<parser::OmpClause>;
if (!std::holds_alternative<ClauseProperty>(property.u)) {
context_.Say(property.source,
"Invalid clause specification for %s"_err_en_US,
parser::ToUpperCaseLetters(getClauseName(clauseId)));
}
}
}
llvm::StringRef OmpStructureChecker::getClauseName(llvm::omp::Clause clause) {
return llvm::omp::getOpenMPClauseName(clause);
}

View File

@@ -184,6 +184,32 @@ private:
// specific clause related
void CheckAllowedMapTypes(const parser::OmpMapType::Value &,
const std::list<parser::OmpMapType::Value> &);
std::optional<evaluate::DynamicType> GetDynamicType(
const common::Indirection<parser::Expr> &);
const std::list<parser::OmpTraitProperty> &GetTraitPropertyList(
const parser::OmpTraitSelector &);
std::optional<llvm::omp::Clause> GetClauseFromProperty(
const parser::OmpTraitProperty &);
void CheckTraitSelectorList(const std::list<parser::OmpTraitSelector> &);
void CheckTraitSetSelector(const parser::OmpTraitSetSelector &);
void CheckTraitScore(const parser::OmpTraitScore &);
bool VerifyTraitPropertyLists(
const parser::OmpTraitSetSelector &, const parser::OmpTraitSelector &);
void CheckTraitSelector(
const parser::OmpTraitSetSelector &, const parser::OmpTraitSelector &);
void CheckTraitADMO(
const parser::OmpTraitSetSelector &, const parser::OmpTraitSelector &);
void CheckTraitCondition(
const parser::OmpTraitSetSelector &, const parser::OmpTraitSelector &);
void CheckTraitDeviceNum(
const parser::OmpTraitSetSelector &, const parser::OmpTraitSelector &);
void CheckTraitRequires(
const parser::OmpTraitSetSelector &, const parser::OmpTraitSelector &);
void CheckTraitSimd(
const parser::OmpTraitSetSelector &, const parser::OmpTraitSelector &);
llvm::StringRef getClauseName(llvm::omp::Clause clause) override;
llvm::StringRef getDirectiveName(llvm::omp::Directive directive) override;

View File

@@ -156,6 +156,23 @@ const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpChunkModifier>() {
return desc;
}
template <>
const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpContextSelector>() {
static const OmpModifierDescriptor desc{
/*name=*/"context-selector",
/*props=*/
{
{50, {OmpProperty::Required, OmpProperty::Unique}},
},
/*clauses=*/
{
// The MATCH clause takes a selector as an argument, not modifier.
{50, {Clause::OMPC_when}},
},
};
return desc;
}
template <>
const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpDependenceType>() {
static const OmpModifierDescriptor desc{

View File

@@ -22,17 +22,17 @@ end
!PARSE-TREE: | | | OmpClauseList ->
subroutine f01
!$omp metadirective when(device={kind(host), device_num(1)}: nothing)
!$omp metadirective when(target_device={kind(host), device_num(1)}: nothing)
end
!UNPARSE: SUBROUTINE f01
!UNPARSE: !$OMP METADIRECTIVE WHEN(DEVICE={KIND(host), DEVICE_NUM(1_4)}: NOTHING)
!UNPARSE: !$OMP METADIRECTIVE WHEN(TARGET_DEVICE={KIND(host), DEVICE_NUM(1_4)}: NOTHING)
!UNPARSE: END SUBROUTINE
!PARSE-TREE: ExecutionPartConstruct -> ExecutableConstruct -> OpenMPConstruct -> OpenMPStandaloneConstruct -> OmpMetadirectiveDirective
!PARSE-TREE: | OmpClauseList -> OmpClause -> When -> OmpWhenClause
!PARSE-TREE: | | Modifier -> OmpContextSelectorSpecification -> OmpTraitSetSelector
!PARSE-TREE: | | | OmpTraitSetSelectorName -> Value = Device
!PARSE-TREE: | | | OmpTraitSetSelectorName -> Value = Target_Device
!PARSE-TREE: | | | OmpTraitSelector
!PARSE-TREE: | | | | OmpTraitSelectorName -> Value = Kind
!PARSE-TREE: | | | | Properties
@@ -95,12 +95,12 @@ end
subroutine f04
!$omp metadirective &
!$omp & when(implementation={extension(haha(1), foo(baz, "bar"(1)))}: nothing)
!$omp when(implementation={extension_trait(haha(1), foo(baz, "bar"(1)))}: nothing)
end
!UNPARSE: SUBROUTINE f04
!UNPARSE: !$OMP METADIRECTIVE WHEN(IMPLEMENTATION={EXTENSION(haha(1_4), foo(baz,bar(1_4)))}: &
!UNPARSE: !$OMP&NOTHING)
!UNPARSE: !$OMP METADIRECTIVE WHEN(IMPLEMENTATION={extension_trait(haha(1_4), foo(baz,bar(1_4&
!UNPARSE: !$OMP&)))}: NOTHING)
!UNPARSE: END SUBROUTINE
!PARSE-TREE: ExecutionPartConstruct -> ExecutableConstruct -> OpenMPConstruct -> OpenMPStandaloneConstruct -> OmpMetadirectiveDirective
@@ -108,7 +108,7 @@ end
!PARSE-TREE: | | Modifier -> OmpContextSelectorSpecification -> OmpTraitSetSelector
!PARSE-TREE: | | | OmpTraitSetSelectorName -> Value = Implementation
!PARSE-TREE: | | | OmpTraitSelector
!PARSE-TREE: | | | | OmpTraitSelectorName -> Value = Extension
!PARSE-TREE: | | | | OmpTraitSelectorName -> string = 'extension_trait'
!PARSE-TREE: | | | | Properties
!PARSE-TREE: | | | | | OmpTraitProperty -> OmpTraitPropertyExtension -> Complex
!PARSE-TREE: | | | | | | OmpTraitPropertyName -> string = 'haha'

View File

@@ -0,0 +1,37 @@
!RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=52
! Common context selector tests
subroutine f00
!$omp metadirective &
!$omp & when(implementation={vendor("this")}, &
!ERROR: Repeated trait set name IMPLEMENTATION in a context specifier
!$omp & implementation={requires(unified_shared_memory)}: nothing)
end
subroutine f01
!$omp metadirective &
!ERROR: Repeated trait name ISA in a trait set
!$omp & when(device={isa("this"), isa("that")}: nothing)
end
subroutine f02
!$omp metadirective &
!ERROR: SCORE expression must be a non-negative constant integer expression
!$omp & when(user={condition(score(-2): .true.)}: nothing)
end
subroutine f03(x)
integer :: x
!$omp metadirective &
!ERROR: SCORE expression must be a non-negative constant integer expression
!$omp & when(user={condition(score(x): .true.)}: nothing)
end
subroutine f04
!$omp metadirective &
!ERROR: Trait property should be a scalar expression
!ERROR: More invalid properties are present
!$omp & when(target_device={device_num("device", "foo"(1))}: nothing)
end

View File

@@ -0,0 +1,33 @@
!RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=52
! The CONSTRUCT trait set
subroutine f00
!$omp metadirective &
!ERROR: CONDITION is not a valid trait for CONSTRUCT trait set
!$omp & when(construct={condition(.true.)}: nothing)
end
subroutine f01
!$omp metadirective &
!ERROR: Directive-name traits cannot have properties
!$omp & when(construct={parallel(nowait), simd}: nothing)
end
subroutine f02
!$omp metadirective &
!ERROR: SIMD trait requires a clause that is allowed on the DECLARE SIMD directive
!$omp & when(construct={simd(nowait)}: nothing)
end
subroutine f03
!$omp metadirective &
!ERROR: Extension traits are not valid for CONSTRUCT trait set
!$omp & when(construct={fred(1)}: nothing)
end
subroutine f04
!$omp metadirective &
!This is ok
!$omp & when(construct={parallel, simd(simdlen(32), notinbranch)}: nothing)
end

View File

@@ -0,0 +1,36 @@
!RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=52
! The DEVICE and TARGET_DEVICE trait sets
subroutine f00
!$omp metadirective &
!ERROR: DEVICE_NUM is not a valid trait for DEVICE trait set
!$omp & when(device={device_num(10)}: nothing)
end
subroutine f01
!$omp metadirective &
!This is ok: all traits are valid
!$omp & when(device={arch("some-arch"), isa("some-isa"), kind("some-kind")}:&
!$omp & nothing)
end
subroutine f02
!$omp metadirective &
!This is ok: all traits are valid
!$omp & when(target_device={arch("some-arch"), device_num(10), &
!$omp & isa("some-isa"), kind("some-kind"), uid("some-uid")}: nothing)
end
subroutine f03
!$omp metadirective &
!This is ok: extension traits are allowed
!$omp & when(device={some_new_trait}: nothing)
end
subroutine f04
!$omp metadirective &
!This is ok: extension traits are allowed
!$omp & when(target_device={another_new_trait(12, 21)}: nothing)
end

View File

@@ -0,0 +1,33 @@
!RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=52
! The IMPLEMENTATION trait set
subroutine f00
!$omp metadirective &
!ERROR: Trait property should be a clause
!$omp & when(implementation={atomic_default_mem_order(0)}: nothing)
end
subroutine f01
!$omp metadirective &
!ERROR: ATOMIC_DEFAULT_MEM_ORDER trait requires a clause from the memory-order clause set
!$omp & when(implementation={atomic_default_mem_order(nowait)}: nothing)
end
subroutine f02
!$omp metadirective &
!ERROR: REQUIRES trait requires a clause from the requirement clause set
!ERROR: Invalid clause specification for SHARED
!$omp & when(implementation={requires(shared)}: nothing)
end
subroutine f03
!$omp metadirective &
!This is ok
!$omp & when(implementation={ &
!$omp & atomic_default_mem_order(relaxed), &
!$omp & extension("foo"), &
!$omp & requires(unified_address),
!$omp & vendor(some_vendor) &
!$omp & }: nothing)
end

View File

@@ -0,0 +1,29 @@
!RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=52
! The USER trait set
subroutine f00(x)
integer :: x
!$omp metadirective &
!ERROR: CONDITION trait requires a single LOGICAL expression
!$omp & when(user={condition(score(2): x)}: nothing)
end
subroutine f01
!$omp metadirective &
!ERROR: CONDITION trait requires a single expression property
!$omp & when(user={condition(.true., .false.)}: nothing)
end
subroutine f02
!$omp metadirective &
!ERROR: Extension traits are not valid for USER trait set
!$omp & when(user={fred}: nothing)
end
subroutine f03(x)
integer :: x
!$omp metadirective &
!This is ok
!$omp & when(user={condition(x > 0)}: nothing)
end