-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[flang][OpenMP] Parse cancel-directive-name as clause #130146
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…ication The `OmpDirectiveName` class has a source in addition to wrapping the llvm::omp::Directive.
Accommodate it in OmpDirectiveSpecification, which may become the primary component of the actual FLUSH construct in the future.
The cancellable construct names on CANCEL or CANCELLATION POINT directives are actually clauses (with the same names as the corresponding constructs). Instead of parsing them into a custom structure, parse them as a clause, which will make CANCEL/CANCELLATION POINT follow the same uniform scheme as other constructs (<directive> [(<arguments>)] [clauses]).
@llvm/pr-subscribers-flang-openmp @llvm/pr-subscribers-flang-semantics Author: Krzysztof Parzyszek (kparzysz) ChangesThe cancellable construct names on CANCEL or CANCELLATION POINT directives are actually clauses (with the same names as the corresponding constructs). Instead of parsing them into a custom structure, parse them as a clause, which will make CANCEL/CANCELLATION POINT follow the same uniform scheme as other constructs (<directive> [(<arguments>)] [clauses]). Full diff: https://github.com/llvm/llvm-project/pull/130146.diff 8 Files Affected:
diff --git a/flang/include/flang/Parser/dump-parse-tree.h b/flang/include/flang/Parser/dump-parse-tree.h
index fcd902d25fa40..004e22a21ecfa 100644
--- a/flang/include/flang/Parser/dump-parse-tree.h
+++ b/flang/include/flang/Parser/dump-parse-tree.h
@@ -546,6 +546,7 @@ class ParseTreeDumper {
#define GEN_FLANG_DUMP_PARSE_TREE_CLAUSES
#include "llvm/Frontend/OpenMP/OMP.inc"
NODE(parser, OmpClauseList)
+ NODE(parser, OmpCancellationConstructTypeClause)
NODE(parser, OmpContainsClause)
NODE(parser, OmpCriticalDirective)
NODE(parser, OmpErrorDirective)
@@ -689,7 +690,6 @@ class ParseTreeDumper {
NODE(parser, OpenMPAtomicConstruct)
NODE(parser, OpenMPBlockConstruct)
NODE(parser, OpenMPCancelConstruct)
- NODE(OpenMPCancelConstruct, If)
NODE(parser, OpenMPCancellationPointConstruct)
NODE(parser, OpenMPConstruct)
NODE(parser, OpenMPCriticalConstruct)
diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h
index a197249ebae91..cb0eb884e1193 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -4048,6 +4048,12 @@ struct OmpBindClause {
WRAPPER_CLASS_BOILERPLATE(OmpBindClause, Binding);
};
+// Artificial clause to represent a cancellable construct.
+struct OmpCancellationConstructTypeClause {
+ TUPLE_CLASS_BOILERPLATE(OmpCancellationConstructTypeClause);
+ std::tuple<OmpDirectiveName, std::optional<ScalarLogicalExpr>> t;
+};
+
// Ref: [5.2:214]
//
// contains-clause ->
@@ -4870,15 +4876,14 @@ struct OmpCancelType {
struct OpenMPCancellationPointConstruct {
TUPLE_CLASS_BOILERPLATE(OpenMPCancellationPointConstruct);
CharBlock source;
- std::tuple<Verbatim, OmpCancelType> t;
+ std::tuple<Verbatim, OmpClauseList> t;
};
// 2.14.1 cancel -> CANCEL construct-type-clause [ [,] if-clause]
struct OpenMPCancelConstruct {
TUPLE_CLASS_BOILERPLATE(OpenMPCancelConstruct);
- WRAPPER_CLASS(If, ScalarLogicalExpr);
CharBlock source;
- std::tuple<Verbatim, OmpCancelType, std::optional<If>> t;
+ std::tuple<Verbatim, OmpClauseList> t;
};
// Ref: [5.0:254-255], [5.1:287-288], [5.2:322-323]
diff --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp
index 80831db0e7d50..51b2567a3894d 100644
--- a/flang/lib/Parser/openmp-parsers.cpp
+++ b/flang/lib/Parser/openmp-parsers.cpp
@@ -803,8 +803,9 @@ TYPE_PARSER(construct<OmpAbsentClause>(many(maybe(","_tok) >>
TYPE_PARSER(construct<OmpContainsClause>(many(maybe(","_tok) >>
construct<llvm::omp::Directive>(unwrap(OmpDirectiveNameParser{})))))
-TYPE_PARSER("ABSENT" >> construct<OmpClause>(construct<OmpClause::Absent>(
- parenthesized(Parser<OmpAbsentClause>{}))) ||
+TYPE_PARSER( //
+ "ABSENT" >> construct<OmpClause>(construct<OmpClause::Absent>(
+ parenthesized(Parser<OmpAbsentClause>{}))) ||
"ACQUIRE" >> construct<OmpClause>(construct<OmpClause::Acquire>()) ||
"ACQ_REL" >> construct<OmpClause>(construct<OmpClause::AcqRel>()) ||
"AFFINITY" >> construct<OmpClause>(construct<OmpClause::Affinity>(
@@ -981,7 +982,24 @@ TYPE_PARSER("ABSENT" >> construct<OmpClause>(construct<OmpClause::Absent>(
"UPDATE" >> construct<OmpClause>(construct<OmpClause::Update>(
parenthesized(Parser<OmpUpdateClause>{}))) ||
"WHEN" >> construct<OmpClause>(construct<OmpClause::When>(
- parenthesized(Parser<OmpWhenClause>{}))))
+ parenthesized(Parser<OmpWhenClause>{}))) ||
+ // Cancellable constructs
+ construct<OmpClause>(construct<OmpClause::CancellationConstructType>(
+ construct<OmpCancellationConstructTypeClause>( //
+ construct<OmpDirectiveName>(verbatim("DO"_id)),
+ maybe(parenthesized(scalarLogicalExpr))))) ||
+ construct<OmpClause>(construct<OmpClause::CancellationConstructType>(
+ construct<OmpCancellationConstructTypeClause>( //
+ construct<OmpDirectiveName>(verbatim("PARALLEL"_id)),
+ maybe(parenthesized(scalarLogicalExpr))))) ||
+ construct<OmpClause>(construct<OmpClause::CancellationConstructType>(
+ construct<OmpCancellationConstructTypeClause>( //
+ construct<OmpDirectiveName>(verbatim("SECTIONS"_id)),
+ maybe(parenthesized(scalarLogicalExpr))))) ||
+ construct<OmpClause>(construct<OmpClause::CancellationConstructType>(
+ construct<OmpCancellationConstructTypeClause>( //
+ construct<OmpDirectiveName>(verbatim("TASKGROUP"_id)),
+ maybe(parenthesized(scalarLogicalExpr))))))
// [Clause, [Clause], ...]
TYPE_PARSER(sourced(construct<OmpClauseList>(
@@ -1104,11 +1122,11 @@ TYPE_PARSER(sourced(construct<OmpCancelType>(
// 2.14.2 Cancellation Point construct
TYPE_PARSER(sourced(construct<OpenMPCancellationPointConstruct>(
- verbatim("CANCELLATION POINT"_tok), Parser<OmpCancelType>{})))
+ verbatim("CANCELLATION POINT"_tok), Parser<OmpClauseList>{})))
// 2.14.1 Cancel construct
TYPE_PARSER(sourced(construct<OpenMPCancelConstruct>(verbatim("CANCEL"_tok),
- Parser<OmpCancelType>{}, maybe("IF" >> parenthesized(scalarLogicalExpr)))))
+ Parser<OmpClauseList>{})))
TYPE_PARSER(sourced(construct<OmpFailClause>(
parenthesized(indirect(Parser<OmpMemoryOrderClause>{})))))
@@ -1192,9 +1210,10 @@ TYPE_PARSER(
sourced(construct<OpenMPStandaloneConstruct>(
Parser<OpenMPSimpleStandaloneConstruct>{}) ||
construct<OpenMPStandaloneConstruct>(Parser<OpenMPFlushConstruct>{}) ||
- construct<OpenMPStandaloneConstruct>(Parser<OpenMPCancelConstruct>{}) ||
+ // Try CANCELLATION POINT before CANCEL.
construct<OpenMPStandaloneConstruct>(
Parser<OpenMPCancellationPointConstruct>{}) ||
+ construct<OpenMPStandaloneConstruct>(Parser<OpenMPCancelConstruct>{}) ||
construct<OpenMPStandaloneConstruct>(
Parser<OmpMetadirectiveDirective>{}) ||
construct<OpenMPStandaloneConstruct>(Parser<OpenMPDepobjConstruct>{})) /
diff --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp
index 12d86653a2b95..11ce214450d96 100644
--- a/flang/lib/Parser/unparse.cpp
+++ b/flang/lib/Parser/unparse.cpp
@@ -2851,15 +2851,14 @@ class UnparseVisitor {
void Unparse(const OpenMPCancellationPointConstruct &x) {
BeginOpenMP();
Word("!$OMP CANCELLATION POINT ");
- Walk(std::get<OmpCancelType>(x.t));
+ Walk(std::get<OmpClauseList>(x.t));
Put("\n");
EndOpenMP();
}
void Unparse(const OpenMPCancelConstruct &x) {
BeginOpenMP();
Word("!$OMP CANCEL ");
- Walk(std::get<OmpCancelType>(x.t));
- Walk(std::get<std::optional<OpenMPCancelConstruct::If>>(x.t));
+ Walk(std::get<OmpClauseList>(x.t));
Put("\n");
EndOpenMP();
}
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 64a0be703744d..8d819defb4a0c 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -2176,9 +2176,13 @@ void OmpStructureChecker::Leave(const parser::OpenMPFlushConstruct &x) {
void OmpStructureChecker::Enter(const parser::OpenMPCancelConstruct &x) {
const auto &dir{std::get<parser::Verbatim>(x.t)};
- const auto &type{std::get<parser::OmpCancelType>(x.t)};
+ const auto &clauses{std::get<parser::OmpClauseList>(x.t)};
PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_cancel);
- CheckCancellationNest(dir.source, type.v);
+
+ if (auto maybeConstruct{GetCancelType(
+ llvm::omp::Directive::OMPD_cancel, x.source, clauses)}) {
+ CheckCancellationNest(dir.source, *maybeConstruct);
+ }
}
void OmpStructureChecker::Leave(const parser::OpenMPCancelConstruct &) {
@@ -2228,13 +2232,38 @@ void OmpStructureChecker::Leave(const parser::OpenMPCriticalConstruct &) {
dirContext_.pop_back();
}
+void OmpStructureChecker::Enter(
+ const parser::OmpClause::CancellationConstructType &x) {
+ // Do not call CheckAllowed/CheckAllowedClause, because in case of an error
+ // it will print "CANCELLATION_CONSTRUCT_TYPE" as the clause name instead of
+ // the contained construct name.
+ auto &dirName{std::get<parser::OmpDirectiveName>(x.v.t)};
+ switch (dirName.v) {
+ case llvm::omp::Directive::OMPD_do:
+ case llvm::omp::Directive::OMPD_parallel:
+ case llvm::omp::Directive::OMPD_sections:
+ case llvm::omp::Directive::OMPD_taskgroup:
+ break;
+ default:
+ context_.Say(dirName.source,
+ "%s is not a cancellable construct"_err_en_US,
+ parser::ToUpperCaseLetters(
+ llvm::omp::getOpenMPDirectiveName(dirName.v).str()));
+ break;
+ }
+}
+
void OmpStructureChecker::Enter(
const parser::OpenMPCancellationPointConstruct &x) {
const auto &dir{std::get<parser::Verbatim>(x.t)};
- const auto &type{std::get<parser::OmpCancelType>(x.t)};
+ const auto &clauses{std::get<parser::OmpClauseList>(x.t)};
PushContextAndClauseSets(
dir.source, llvm::omp::Directive::OMPD_cancellation_point);
- CheckCancellationNest(dir.source, type.v);
+
+ if (auto maybeConstruct{GetCancelType(
+ llvm::omp::Directive::OMPD_cancellation_point, x.source, clauses)}) {
+ CheckCancellationNest(dir.source, *maybeConstruct);
+ }
}
void OmpStructureChecker::Leave(
@@ -2242,8 +2271,42 @@ void OmpStructureChecker::Leave(
dirContext_.pop_back();
}
+std::optional<llvm::omp::Directive> OmpStructureChecker::GetCancelType(
+ llvm::omp::Directive cancelDir, const parser::CharBlock &cancelSource,
+ const parser::OmpClauseList &clauses) {
+ // Given clauses from CANCEL or CANCELLATION_POINT, identify the construct
+ // to which the cancellation applies.
+ std::optional<llvm::omp::Directive> cancelee;
+ llvm::StringRef cancelName{llvm::omp::getOpenMPDirectiveName(cancelDir)};
+
+ for (const parser::OmpClause &clause : clauses.v) {
+ using CancellationConstructType =
+ parser::OmpClause::CancellationConstructType;
+ if (auto *cctype{std::get_if<CancellationConstructType>(&clause.u)}) {
+ if (cancelee) {
+ context_.Say(cancelSource,
+ "Multiple cancel-directive-name clauses are not allowed on the %s construct"_err_en_US,
+ parser::ToUpperCaseLetters(cancelName.str()));
+ return std::nullopt;
+ }
+ cancelee = std::get<parser::OmpDirectiveName>(cctype->v.t).v;
+ }
+ }
+
+ if (!cancelee) {
+ context_.Say(cancelSource,
+ "Missing cancel-directive-name clause on the %s construct"_err_en_US,
+ parser::ToUpperCaseLetters(cancelName.str()));
+ return std::nullopt;
+ }
+
+ return cancelee;
+}
+
void OmpStructureChecker::CheckCancellationNest(
- const parser::CharBlock &source, const parser::OmpCancelType::Type &type) {
+ const parser::CharBlock &source, llvm::omp::Directive type) {
+ llvm::StringRef typeName{llvm::omp::getOpenMPDirectiveName(type)};
+
if (CurrentDirectiveIsNested()) {
// If construct-type-clause is taskgroup, the cancellation construct must be
// closely nested inside a task or a taskloop construct and the cancellation
@@ -2254,8 +2317,9 @@ void OmpStructureChecker::CheckCancellationNest(
// that matches the type specified in construct-type-clause of the
// cancellation construct.
bool eligibleCancellation{false};
+
switch (type) {
- case parser::OmpCancelType::Type::Taskgroup:
+ case llvm::omp::Directive::OMPD_taskgroup:
if (llvm::omp::nestedCancelTaskgroupAllowedSet.test(
GetContextParent().directive)) {
eligibleCancellation = true;
@@ -2281,38 +2345,37 @@ void OmpStructureChecker::CheckCancellationNest(
}
if (!eligibleCancellation) {
context_.Say(source,
- "With %s clause, %s construct must be closely nested inside TASK "
- "or TASKLOOP construct and %s region must be closely nested inside "
- "TASKGROUP region"_err_en_US,
- parser::ToUpperCaseLetters(
- parser::OmpCancelType::EnumToString(type)),
+ "With %s clause, %s construct must be closely nested inside TASK or TASKLOOP construct and %s region must be closely nested inside TASKGROUP region"_err_en_US,
+ parser::ToUpperCaseLetters(typeName.str()),
ContextDirectiveAsFortran(), ContextDirectiveAsFortran());
}
return;
- case parser::OmpCancelType::Type::Sections:
+ case llvm::omp::Directive::OMPD_sections:
if (llvm::omp::nestedCancelSectionsAllowedSet.test(
GetContextParent().directive)) {
eligibleCancellation = true;
}
break;
- case parser::OmpCancelType::Type::Do:
+ case llvm::omp::Directive::OMPD_do:
if (llvm::omp::nestedCancelDoAllowedSet.test(
GetContextParent().directive)) {
eligibleCancellation = true;
}
break;
- case parser::OmpCancelType::Type::Parallel:
+ case llvm::omp::Directive::OMPD_parallel:
if (llvm::omp::nestedCancelParallelAllowedSet.test(
GetContextParent().directive)) {
eligibleCancellation = true;
}
break;
+ default:
+ // This should have been diagnosed by this point.
+ llvm_unreachable("Unexpected directive");
}
if (!eligibleCancellation) {
context_.Say(source,
- "With %s clause, %s construct cannot be closely nested inside %s "
- "construct"_err_en_US,
- parser::ToUpperCaseLetters(parser::OmpCancelType::EnumToString(type)),
+ "With %s clause, %s construct cannot be closely nested inside %s construct"_err_en_US,
+ parser::ToUpperCaseLetters(typeName.str()),
ContextDirectiveAsFortran(),
parser::ToUpperCaseLetters(
getDirectiveName(GetContextParent().directive).str()));
@@ -2320,38 +2383,33 @@ void OmpStructureChecker::CheckCancellationNest(
} else {
// The cancellation directive cannot be orphaned.
switch (type) {
- case parser::OmpCancelType::Type::Taskgroup:
+ case llvm::omp::Directive::OMPD_taskgroup:
context_.Say(source,
- "%s %s directive is not closely nested inside "
- "TASK or TASKLOOP"_err_en_US,
+ "%s %s directive is not closely nested inside TASK or TASKLOOP"_err_en_US,
ContextDirectiveAsFortran(),
- parser::ToUpperCaseLetters(
- parser::OmpCancelType::EnumToString(type)));
+ parser::ToUpperCaseLetters(typeName.str()));
break;
- case parser::OmpCancelType::Type::Sections:
+ case llvm::omp::Directive::OMPD_sections:
context_.Say(source,
- "%s %s directive is not closely nested inside "
- "SECTION or SECTIONS"_err_en_US,
+ "%s %s directive is not closely nested inside SECTION or SECTIONS"_err_en_US,
ContextDirectiveAsFortran(),
- parser::ToUpperCaseLetters(
- parser::OmpCancelType::EnumToString(type)));
+ parser::ToUpperCaseLetters(typeName.str()));
break;
- case parser::OmpCancelType::Type::Do:
+ case llvm::omp::Directive::OMPD_do:
context_.Say(source,
- "%s %s directive is not closely nested inside "
- "the construct that matches the DO clause type"_err_en_US,
+ "%s %s directive is not closely nested inside the construct that matches the DO clause type"_err_en_US,
ContextDirectiveAsFortran(),
- parser::ToUpperCaseLetters(
- parser::OmpCancelType::EnumToString(type)));
+ parser::ToUpperCaseLetters(typeName.str()));
break;
- case parser::OmpCancelType::Type::Parallel:
+ case llvm::omp::Directive::OMPD_parallel:
context_.Say(source,
- "%s %s directive is not closely nested inside "
- "the construct that matches the PARALLEL clause type"_err_en_US,
+ "%s %s directive is not closely nested inside the construct that matches the PARALLEL clause type"_err_en_US,
ContextDirectiveAsFortran(),
- parser::ToUpperCaseLetters(
- parser::OmpCancelType::EnumToString(type)));
+ parser::ToUpperCaseLetters(typeName.str()));
break;
+ default:
+ // This should have been diagnosed by this point.
+ llvm_unreachable("Unexpected directive");
}
}
}
@@ -3044,7 +3102,6 @@ CHECK_SIMPLE_CLAUSE(MemoryOrder, OMPC_memory_order)
CHECK_SIMPLE_CLAUSE(Bind, OMPC_bind)
CHECK_SIMPLE_CLAUSE(Align, OMPC_align)
CHECK_SIMPLE_CLAUSE(Compare, OMPC_compare)
-CHECK_SIMPLE_CLAUSE(CancellationConstructType, OMPC_cancellation_construct_type)
CHECK_SIMPLE_CLAUSE(OmpxAttribute, OMPC_ompx_attribute)
CHECK_SIMPLE_CLAUSE(Weak, OMPC_weak)
diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index b70ea58cf5578..48795995fdf50 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -275,8 +275,11 @@ class OmpStructureChecker
void CheckTargetUpdate();
void CheckDependenceType(const parser::OmpDependenceType::Value &x);
void CheckTaskDependenceType(const parser::OmpTaskDependenceType::Value &x);
+ std::optional<llvm::omp::Directive> GetCancelType(
+ llvm::omp::Directive cancelDir, const parser::CharBlock &cancelSource,
+ const parser::OmpClauseList &clauses);
void CheckCancellationNest(
- const parser::CharBlock &source, const parser::OmpCancelType::Type &type);
+ const parser::CharBlock &source, llvm::omp::Directive type);
std::int64_t GetOrdCollapseLevel(const parser::OpenMPLoopConstruct &x);
void CheckReductionObjects(
const parser::OmpObjectList &objects, llvm::omp::Clause clauseId);
diff --git a/flang/test/Semantics/OpenMP/cancel.f90 b/flang/test/Semantics/OpenMP/cancel.f90
new file mode 100644
index 0000000000000..581c4bdb97646
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/cancel.f90
@@ -0,0 +1,29 @@
+!RUN: %python %S/../test_errors.py %s %flang -fopenmp
+
+subroutine f00
+!$omp parallel
+!ERROR: Missing cancel-directive-name clause on the CANCEL construct
+!$omp cancel
+!$omp end parallel
+end
+
+subroutine f01
+!$omp parallel
+!ERROR: Multiple cancel-directive-name clauses are not allowed on the CANCEL construct
+!$omp cancel parallel parallel
+!$omp end parallel
+end
+
+subroutine f02
+!$omp parallel
+!ERROR: Missing cancel-directive-name clause on the CANCELLATION POINT construct
+!$omp cancellation point
+!$omp end parallel
+end
+
+subroutine f03
+!$omp parallel
+!ERROR: Multiple cancel-directive-name clauses are not allowed on the CANCELLATION POINT construct
+!$omp cancellation point parallel parallel
+!$omp end parallel
+end
diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td
index 8a2f30a7995dc..cbad02a70609f 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMP.td
+++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td
@@ -105,6 +105,7 @@ def OMPC_CancellationConstructType : Clause<"cancellation_construct_type"> {
OMP_CANCELLATION_CONSTRUCT_Taskgroup,
OMP_CANCELLATION_CONSTRUCT_None
];
+ let flangClass = "OmpCancellationConstructTypeClause";
}
def OMPC_Contains : Clause<"contains"> {
let clangClass = "OMPContainsClause";
@@ -647,12 +648,16 @@ def OMP_BeginDeclareVariant : Directive<"begin declare variant"> {
}
def OMP_Cancel : Directive<"cancel"> {
let allowedOnceClauses = [
+ VersionedClause<OMPC_CancellationConstructType>,
VersionedClause<OMPC_If>,
];
let association = AS_None;
let category = CA_Executable;
}
def OMP_CancellationPoint : Directive<"cancellation point"> {
+ let allowedOnceClauses = [
+ VersionedClause<OMPC_CancellationConstructType>,
+ ];
let association = AS_None;
let category = CA_Executable;
}
|
…ysz/spr/o02-metadirective-flush
…kparzysz/spr/o03-cancel-directive-name
…ysz/spr/o02-metadirective-flush
…kparzysz/spr/o03-cancel-directive-name
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LG.
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/51/builds/12276 Here is the relevant piece of the build log for the reference
|
The cancellable construct names on CANCEL or CANCELLATION POINT directives are actually clauses (with the same names as the corresponding constructs).
Instead of parsing them into a custom structure, parse them as a clause, which will make CANCEL/CANCELLATION POINT follow the same uniform scheme as other constructs ( [()] [clauses]).