From: Alexey Bataev Date: Thu, 17 Jul 2014 07:32:53 +0000 (+0000) Subject: [OPENMP] Initial parsing and sema analysis for 'final' clause. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=ff45f6913bdc201f4e36d54f38538672070c9eb3;p=clang [OPENMP] Initial parsing and sema analysis for 'final' clause. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@213232 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/DataRecursiveASTVisitor.h b/include/clang/AST/DataRecursiveASTVisitor.h index a84be8ad2b..70bf06b513 100644 --- a/include/clang/AST/DataRecursiveASTVisitor.h +++ b/include/clang/AST/DataRecursiveASTVisitor.h @@ -2330,6 +2330,12 @@ bool RecursiveASTVisitor::VisitOMPIfClause(OMPIfClause *C) { return true; } +template +bool RecursiveASTVisitor::VisitOMPFinalClause(OMPFinalClause *C) { + TRY_TO(TraverseStmt(C->getCondition())); + return true; +} + template bool RecursiveASTVisitor::VisitOMPNumThreadsClause(OMPNumThreadsClause *C) { diff --git a/include/clang/AST/OpenMPClause.h b/include/clang/AST/OpenMPClause.h index 95cb11ce64..35431753f4 100644 --- a/include/clang/AST/OpenMPClause.h +++ b/include/clang/AST/OpenMPClause.h @@ -196,6 +196,59 @@ public: StmtRange children() { return StmtRange(&Condition, &Condition + 1); } }; +/// \brief This represents 'final' clause in the '#pragma omp ...' directive. +/// +/// \code +/// #pragma omp task final(a > 5) +/// \endcode +/// In this example directive '#pragma omp task' has simple 'final' +/// clause with condition 'a > 5'. +/// +class OMPFinalClause : public OMPClause { + friend class OMPClauseReader; + /// \brief Location of '('. + SourceLocation LParenLoc; + /// \brief Condition of the 'if' clause. + Stmt *Condition; + + /// \brief Set condition. + /// + void setCondition(Expr *Cond) { Condition = Cond; } + +public: + /// \brief Build 'final' clause with condition \a Cond. + /// + /// \param StartLoc Starting location of the clause. + /// \param LParenLoc Location of '('. + /// \param Cond Condition of the clause. + /// \param EndLoc Ending location of the clause. + /// + OMPFinalClause(Expr *Cond, SourceLocation StartLoc, SourceLocation LParenLoc, + SourceLocation EndLoc) + : OMPClause(OMPC_final, StartLoc, EndLoc), LParenLoc(LParenLoc), + Condition(Cond) {} + + /// \brief Build an empty clause. + /// + OMPFinalClause() + : OMPClause(OMPC_final, SourceLocation(), SourceLocation()), + LParenLoc(SourceLocation()), Condition(nullptr) {} + + /// \brief Sets the location of '('. + void setLParenLoc(SourceLocation Loc) { LParenLoc = Loc; } + /// \brief Returns the location of '('. + SourceLocation getLParenLoc() const { return LParenLoc; } + + /// \brief Returns condition. + Expr *getCondition() const { return cast_or_null(Condition); } + + static bool classof(const OMPClause *T) { + return T->getClauseKind() == OMPC_final; + } + + StmtRange children() { return StmtRange(&Condition, &Condition + 1); } +}; + /// \brief This represents 'num_threads' clause in the '#pragma omp ...' /// directive. /// diff --git a/include/clang/AST/RecursiveASTVisitor.h b/include/clang/AST/RecursiveASTVisitor.h index 2cb8ea1786..833cfbe6f9 100644 --- a/include/clang/AST/RecursiveASTVisitor.h +++ b/include/clang/AST/RecursiveASTVisitor.h @@ -2352,6 +2352,12 @@ bool RecursiveASTVisitor::VisitOMPIfClause(OMPIfClause *C) { return true; } +template +bool RecursiveASTVisitor::VisitOMPFinalClause(OMPFinalClause *C) { + TRY_TO(TraverseStmt(C->getCondition())); + return true; +} + template bool RecursiveASTVisitor::VisitOMPNumThreadsClause(OMPNumThreadsClause *C) { diff --git a/include/clang/Basic/OpenMPKinds.def b/include/clang/Basic/OpenMPKinds.def index 85295b4c78..ff08b1432a 100644 --- a/include/clang/Basic/OpenMPKinds.def +++ b/include/clang/Basic/OpenMPKinds.def @@ -69,6 +69,7 @@ OPENMP_DIRECTIVE_EXT(parallel_sections, "parallel sections") // OpenMP clauses. OPENMP_CLAUSE(if, OMPIfClause) +OPENMP_CLAUSE(final, OMPFinalClause) OPENMP_CLAUSE(num_threads, OMPNumThreadsClause) OPENMP_CLAUSE(safelen, OMPSafelenClause) OPENMP_CLAUSE(collapse, OMPCollapseClause) @@ -175,6 +176,7 @@ OPENMP_PARALLEL_SECTIONS_CLAUSE(lastprivate) // TODO more clauses allowed for OpenMP directive 'task'. OPENMP_TASK_CLAUSE(if) +OPENMP_TASK_CLAUSE(final) OPENMP_TASK_CLAUSE(default) OPENMP_TASK_CLAUSE(private) OPENMP_TASK_CLAUSE(firstprivate) diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index b8b9eaf970..bb8887a82a 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -7374,6 +7374,10 @@ public: OMPClause *ActOnOpenMPIfClause(Expr *Condition, SourceLocation StartLoc, SourceLocation LParenLoc, SourceLocation EndLoc); + /// \brief Called on well-formed 'final' clause. + OMPClause *ActOnOpenMPFinalClause(Expr *Condition, SourceLocation StartLoc, + SourceLocation LParenLoc, + SourceLocation EndLoc); /// \brief Called on well-formed 'num_threads' clause. OMPClause *ActOnOpenMPNumThreadsClause(Expr *NumThreads, SourceLocation StartLoc, diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp index bb211aa297..5958dacf3a 100644 --- a/lib/AST/StmtPrinter.cpp +++ b/lib/AST/StmtPrinter.cpp @@ -603,6 +603,12 @@ void OMPClausePrinter::VisitOMPIfClause(OMPIfClause *Node) { OS << ")"; } +void OMPClausePrinter::VisitOMPFinalClause(OMPFinalClause *Node) { + OS << "final("; + Node->getCondition()->printPretty(OS, nullptr, Policy, 0); + OS << ")"; +} + void OMPClausePrinter::VisitOMPNumThreadsClause(OMPNumThreadsClause *Node) { OS << "num_threads("; Node->getNumThreads()->printPretty(OS, nullptr, Policy, 0); diff --git a/lib/AST/StmtProfile.cpp b/lib/AST/StmtProfile.cpp index f5be846b36..d0c39a5f34 100644 --- a/lib/AST/StmtProfile.cpp +++ b/lib/AST/StmtProfile.cpp @@ -273,6 +273,11 @@ void OMPClauseProfiler::VisitOMPIfClause(const OMPIfClause *C) { Profiler->VisitStmt(C->getCondition()); } +void OMPClauseProfiler::VisitOMPFinalClause(const OMPFinalClause *C) { + if (C->getCondition()) + Profiler->VisitStmt(C->getCondition()); +} + void OMPClauseProfiler::VisitOMPNumThreadsClause(const OMPNumThreadsClause *C) { if (C->getNumThreads()) Profiler->VisitStmt(C->getNumThreads()); diff --git a/lib/Basic/OpenMPKinds.cpp b/lib/Basic/OpenMPKinds.cpp index 188bd2dbfd..056d358707 100644 --- a/lib/Basic/OpenMPKinds.cpp +++ b/lib/Basic/OpenMPKinds.cpp @@ -88,6 +88,7 @@ unsigned clang::getOpenMPSimpleClauseType(OpenMPClauseKind Kind, case OMPC_unknown: case OMPC_threadprivate: case OMPC_if: + case OMPC_final: case OMPC_num_threads: case OMPC_safelen: case OMPC_collapse: @@ -143,6 +144,7 @@ const char *clang::getOpenMPSimpleClauseTypeName(OpenMPClauseKind Kind, case OMPC_unknown: case OMPC_threadprivate: case OMPC_if: + case OMPC_final: case OMPC_num_threads: case OMPC_safelen: case OMPC_collapse: diff --git a/lib/Parse/ParseOpenMP.cpp b/lib/Parse/ParseOpenMP.cpp index caa8d8f782..e50f4714e4 100644 --- a/lib/Parse/ParseOpenMP.cpp +++ b/lib/Parse/ParseOpenMP.cpp @@ -286,11 +286,11 @@ bool Parser::ParseOpenMPSimpleVarList(OpenMPDirectiveKind Kind, /// \brief Parsing of OpenMP clauses. /// /// clause: -/// if-clause | num_threads-clause | safelen-clause | default-clause | -/// private-clause | firstprivate-clause | shared-clause | linear-clause | -/// aligned-clause | collapse-clause | lastprivate-clause | -/// reduction-clause | proc_bind-clause | schedule-clause | -/// copyin-clause | copyprivate-clause +/// if-clause | final-clause | num_threads-clause | safelen-clause | +/// default-clause | private-clause | firstprivate-clause | shared-clause +/// | linear-clause | aligned-clause | collapse-clause | +/// lastprivate-clause | reduction-clause | proc_bind-clause | +/// schedule-clause | copyin-clause | copyprivate-clause /// OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind DKind, OpenMPClauseKind CKind, bool FirstClause) { @@ -305,6 +305,7 @@ OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind DKind, switch (CKind) { case OMPC_if: + case OMPC_final: case OMPC_num_threads: case OMPC_safelen: case OMPC_collapse: @@ -314,6 +315,9 @@ OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind DKind, // OpenMP [2.8.1, simd construct, Restrictions] // Only one safelen clause can appear on a simd directive. // Only one collapse clause can appear on a simd directive. + // OpenMP [2.11.1, task Construct, Restrictions] + // At most one if clause can appear on the directive. + // At most one final clause can appear on the directive. if (!FirstClause) { Diag(Tok, diag::err_omp_more_one_clause) << getOpenMPDirectiveName(DKind) << getOpenMPClauseName(CKind); @@ -384,12 +388,15 @@ OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind DKind, } /// \brief Parsing of OpenMP clauses with single expressions like 'if', -/// 'collapse', 'safelen', 'num_threads', 'simdlen', 'num_teams' or +/// 'final', 'collapse', 'safelen', 'num_threads', 'simdlen', 'num_teams' or /// 'thread_limit'. /// /// if-clause: /// 'if' '(' expression ')' /// +/// final-clause: +/// 'final' '(' expression ')' +/// /// num_threads-clause: /// 'num_threads' '(' expression ')' /// diff --git a/lib/Sema/SemaOpenMP.cpp b/lib/Sema/SemaOpenMP.cpp index 0dfec26692..58f9bbed6f 100644 --- a/lib/Sema/SemaOpenMP.cpp +++ b/lib/Sema/SemaOpenMP.cpp @@ -1992,6 +1992,9 @@ OMPClause *Sema::ActOnOpenMPSingleExprClause(OpenMPClauseKind Kind, Expr *Expr, case OMPC_if: Res = ActOnOpenMPIfClause(Expr, StartLoc, LParenLoc, EndLoc); break; + case OMPC_final: + Res = ActOnOpenMPFinalClause(Expr, StartLoc, LParenLoc, EndLoc); + break; case OMPC_num_threads: Res = ActOnOpenMPNumThreadsClause(Expr, StartLoc, LParenLoc, EndLoc); break; @@ -2040,6 +2043,25 @@ OMPClause *Sema::ActOnOpenMPIfClause(Expr *Condition, SourceLocation StartLoc, return new (Context) OMPIfClause(ValExpr, StartLoc, LParenLoc, EndLoc); } +OMPClause *Sema::ActOnOpenMPFinalClause(Expr *Condition, + SourceLocation StartLoc, + SourceLocation LParenLoc, + SourceLocation EndLoc) { + Expr *ValExpr = Condition; + if (!Condition->isValueDependent() && !Condition->isTypeDependent() && + !Condition->isInstantiationDependent() && + !Condition->containsUnexpandedParameterPack()) { + ExprResult Val = ActOnBooleanCondition(DSAStack->getCurScope(), + Condition->getExprLoc(), Condition); + if (Val.isInvalid()) + return nullptr; + + ValExpr = Val.get(); + } + + return new (Context) OMPFinalClause(ValExpr, StartLoc, LParenLoc, EndLoc); +} + ExprResult Sema::PerformOpenMPImplicitIntegerConversion(SourceLocation Loc, Expr *Op) { if (!Op) @@ -2180,6 +2202,7 @@ OMPClause *Sema::ActOnOpenMPSimpleClause( LParenLoc, EndLoc); break; case OMPC_if: + case OMPC_final: case OMPC_num_threads: case OMPC_safelen: case OMPC_collapse: @@ -2290,6 +2313,7 @@ OMPClause *Sema::ActOnOpenMPSingleExprWithArgClause( LParenLoc, ArgumentLoc, CommaLoc, EndLoc); break; case OMPC_if: + case OMPC_final: case OMPC_num_threads: case OMPC_safelen: case OMPC_collapse: @@ -2381,6 +2405,7 @@ OMPClause *Sema::ActOnOpenMPClause(OpenMPClauseKind Kind, Res = ActOnOpenMPNowaitClause(StartLoc, EndLoc); break; case OMPC_if: + case OMPC_final: case OMPC_num_threads: case OMPC_safelen: case OMPC_collapse: @@ -2451,6 +2476,7 @@ OMPClause *Sema::ActOnOpenMPVarListClause( Res = ActOnOpenMPCopyprivateClause(VarList, StartLoc, LParenLoc, EndLoc); break; case OMPC_if: + case OMPC_final: case OMPC_num_threads: case OMPC_safelen: case OMPC_collapse: diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index 1a71c0a4a5..f9775c1ad1 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -1318,6 +1318,17 @@ public: LParenLoc, EndLoc); } + /// \brief Build a new OpenMP 'final' clause. + /// + /// By default, performs semantic analysis to build the new OpenMP clause. + /// Subclasses may override this routine to provide different behavior. + OMPClause *RebuildOMPFinalClause(Expr *Condition, SourceLocation StartLoc, + SourceLocation LParenLoc, + SourceLocation EndLoc) { + return getSema().ActOnOpenMPFinalClause(Condition, StartLoc, LParenLoc, + EndLoc); + } + /// \brief Build a new OpenMP 'num_threads' clause. /// /// By default, performs semantic analysis to build the new OpenMP clause. @@ -6542,6 +6553,15 @@ OMPClause *TreeTransform::TransformOMPIfClause(OMPIfClause *C) { C->getLParenLoc(), C->getLocEnd()); } +template +OMPClause *TreeTransform::TransformOMPFinalClause(OMPFinalClause *C) { + ExprResult Cond = getDerived().TransformExpr(C->getCondition()); + if (Cond.isInvalid()) + return nullptr; + return getDerived().RebuildOMPFinalClause(Cond.get(), C->getLocStart(), + C->getLParenLoc(), C->getLocEnd()); +} + template OMPClause * TreeTransform::TransformOMPNumThreadsClause(OMPNumThreadsClause *C) { diff --git a/lib/Serialization/ASTReaderStmt.cpp b/lib/Serialization/ASTReaderStmt.cpp index 29f0555b5f..e719bbb514 100644 --- a/lib/Serialization/ASTReaderStmt.cpp +++ b/lib/Serialization/ASTReaderStmt.cpp @@ -1682,6 +1682,9 @@ OMPClause *OMPClauseReader::readClause() { case OMPC_if: C = new (Context) OMPIfClause(); break; + case OMPC_final: + C = new (Context) OMPFinalClause(); + break; case OMPC_num_threads: C = new (Context) OMPNumThreadsClause(); break; @@ -1746,6 +1749,11 @@ void OMPClauseReader::VisitOMPIfClause(OMPIfClause *C) { C->setLParenLoc(Reader->ReadSourceLocation(Record, Idx)); } +void OMPClauseReader::VisitOMPFinalClause(OMPFinalClause *C) { + C->setCondition(Reader->Reader.ReadSubExpr()); + C->setLParenLoc(Reader->ReadSourceLocation(Record, Idx)); +} + void OMPClauseReader::VisitOMPNumThreadsClause(OMPNumThreadsClause *C) { C->setNumThreads(Reader->Reader.ReadSubExpr()); C->setLParenLoc(Reader->ReadSourceLocation(Record, Idx)); diff --git a/lib/Serialization/ASTWriterStmt.cpp b/lib/Serialization/ASTWriterStmt.cpp index 044ceb7914..cacb2ec185 100644 --- a/lib/Serialization/ASTWriterStmt.cpp +++ b/lib/Serialization/ASTWriterStmt.cpp @@ -1687,6 +1687,11 @@ void OMPClauseWriter::VisitOMPIfClause(OMPIfClause *C) { Writer->Writer.AddSourceLocation(C->getLParenLoc(), Record); } +void OMPClauseWriter::VisitOMPFinalClause(OMPFinalClause *C) { + Writer->Writer.AddStmt(C->getCondition()); + Writer->Writer.AddSourceLocation(C->getLParenLoc(), Record); +} + void OMPClauseWriter::VisitOMPNumThreadsClause(OMPNumThreadsClause *C) { Writer->Writer.AddStmt(C->getNumThreads()); Writer->Writer.AddSourceLocation(C->getLParenLoc(), Record); diff --git a/test/OpenMP/task_ast_print.cpp b/test/OpenMP/task_ast_print.cpp index cf4198ebfa..c868e74977 100644 --- a/test/OpenMP/task_ast_print.cpp +++ b/test/OpenMP/task_ast_print.cpp @@ -35,7 +35,7 @@ T tmain(T argc, T *argv) { S s; #pragma omp task a = 2; -#pragma omp task default(none), private(argc, b) firstprivate(argv) shared(d) if (argc > 0) +#pragma omp task default(none), private(argc, b) firstprivate(argv) shared(d) if (argc > 0) final(S::TS > 0) foo(); #pragma omp task if (C) foo(); @@ -48,7 +48,7 @@ T tmain(T argc, T *argv) { // CHECK-NEXT: S s; // CHECK-NEXT: #pragma omp task // CHECK-NEXT: a = 2; -// CHECK-NEXT: #pragma omp task default(none) private(argc,b) firstprivate(argv) shared(d) if(argc > 0) +// CHECK-NEXT: #pragma omp task default(none) private(argc,b) firstprivate(argv) shared(d) if(argc > 0) final(S::TS > 0) // CHECK-NEXT: foo() // CHECK-NEXT: #pragma omp task if(5) // CHECK-NEXT: foo() @@ -58,7 +58,7 @@ T tmain(T argc, T *argv) { // CHECK-NEXT: S s; // CHECK-NEXT: #pragma omp task // CHECK-NEXT: a = 2; -// CHECK-NEXT: #pragma omp task default(none) private(argc,b) firstprivate(argv) shared(d) if(argc > 0) +// CHECK-NEXT: #pragma omp task default(none) private(argc,b) firstprivate(argv) shared(d) if(argc > 0) final(S::TS > 0) // CHECK-NEXT: foo() // CHECK-NEXT: #pragma omp task if(1) // CHECK-NEXT: foo() @@ -68,7 +68,7 @@ T tmain(T argc, T *argv) { // CHECK-NEXT: S s; // CHECK-NEXT: #pragma omp task // CHECK-NEXT: a = 2; -// CHECK-NEXT: #pragma omp task default(none) private(argc,b) firstprivate(argv) shared(d) if(argc > 0) +// CHECK-NEXT: #pragma omp task default(none) private(argc,b) firstprivate(argv) shared(d) if(argc > 0) final(S::TS > 0) // CHECK-NEXT: foo() // CHECK-NEXT: #pragma omp task if(C) // CHECK-NEXT: foo() @@ -86,8 +86,8 @@ int main(int argc, char **argv) { // CHECK-NEXT: #pragma omp task a = 2; // CHECK-NEXT: a = 2; -#pragma omp task default(none), private(argc, b) firstprivate(argv) if (argc > 0) - // CHECK-NEXT: #pragma omp task default(none) private(argc,b) firstprivate(argv) if(argc > 0) +#pragma omp task default(none), private(argc, b) firstprivate(argv) if (argc > 0) final(a > 0) + // CHECK-NEXT: #pragma omp task default(none) private(argc,b) firstprivate(argv) if(argc > 0) final(a > 0) foo(); // CHECK-NEXT: foo(); return tmain(b, &b) + tmain(x, &x); diff --git a/test/OpenMP/task_final_messages.cpp b/test/OpenMP/task_final_messages.cpp new file mode 100644 index 0000000000..0b52e6aca1 --- /dev/null +++ b/test/OpenMP/task_final_messages.cpp @@ -0,0 +1,46 @@ +// RUN: %clang_cc1 -verify -fopenmp=libiomp5 -ferror-limit 100 %s + +void foo() { +} + +bool foobool(int argc) { + return argc; +} + +struct S1; // expected-note {{declared here}} + +template // expected-note {{declared here}} +int tmain(T argc, S **argv) { + #pragma omp task final // expected-error {{expected '(' after 'final'}} + #pragma omp task final ( // expected-error {{expected expression}} expected-error {{expected ')'}} expected-note {{to match this '('}} + #pragma omp task final () // expected-error {{expected expression}} + #pragma omp task final (argc // expected-error {{expected ')'}} expected-note {{to match this '('}} + #pragma omp task final (argc)) // expected-warning {{extra tokens at the end of '#pragma omp task' are ignored}} + #pragma omp task final (argc > 0 ? argv[1] : argv[2]) + #pragma omp task final (foobool(argc)), final (true) // expected-error {{directive '#pragma omp task' cannot contain more than one 'final' clause}} + #pragma omp task final (S) // expected-error {{'S' does not refer to a value}} + #pragma omp task final (argv[1]=2) // expected-error {{expected ')'}} expected-note {{to match this '('}} + #pragma omp task final (argc argc) // expected-error {{expected ')'}} expected-note {{to match this '('}} + #pragma omp task final(argc) + foo(); + + return 0; +} + +int main(int argc, char **argv) { + #pragma omp task final // expected-error {{expected '(' after 'final'}} + #pragma omp task final ( // expected-error {{expected expression}} expected-error {{expected ')'}} expected-note {{to match this '('}} + #pragma omp task final () // expected-error {{expected expression}} + #pragma omp task final (argc // expected-error {{expected ')'}} expected-note {{to match this '('}} + #pragma omp task final (argc)) // expected-warning {{extra tokens at the end of '#pragma omp task' are ignored}} + #pragma omp task final (argc > 0 ? argv[1] : argv[2]) + #pragma omp task final (foobool(argc)), final (true) // expected-error {{directive '#pragma omp task' cannot contain more than one 'final' clause}} + #pragma omp task final (S1) // expected-error {{'S1' does not refer to a value}} + #pragma omp task final (argv[1]=2) // expected-error {{expected ')'}} expected-note {{to match this '('}} + #pragma omp task final (argc argc) // expected-error {{expected ')'}} expected-note {{to match this '('}} + #pragma omp task final (1 0) // expected-error {{expected ')'}} expected-note {{to match this '('}} + #pragma omp task final(if(tmain(argc, argv) // expected-error {{expected expression}} expected-error {{expected ')'}} expected-note {{to match this '('}} + foo(); + + return tmain(argc, argv); +} diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index 56d761f3a0..4502017158 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -1939,6 +1939,10 @@ void OMPClauseEnqueue::VisitOMPIfClause(const OMPIfClause *C) { Visitor->AddStmt(C->getCondition()); } +void OMPClauseEnqueue::VisitOMPFinalClause(const OMPFinalClause *C) { + Visitor->AddStmt(C->getCondition()); +} + void OMPClauseEnqueue::VisitOMPNumThreadsClause(const OMPNumThreadsClause *C) { Visitor->AddStmt(C->getNumThreads()); }