diff --git a/clang/include/clang/AST/OpenMPClause.h b/clang/include/clang/AST/OpenMPClause.h --- a/clang/include/clang/AST/OpenMPClause.h +++ b/clang/include/clang/AST/OpenMPClause.h @@ -1563,6 +1563,85 @@ } }; +/// This represents 'at' clause in the '#pragma omp error' directive +/// +/// \code +/// #pragma omp error at(compilation) +/// \endcode +/// In this example directive '#pragma omp error' has simple +/// 'at' clause with kind 'complilation'. +class OMPAtClause final : public OMPClause { + friend class OMPClauseReader; + + /// Location of '(' + SourceLocation LParenLoc; + + /// A kind of the 'at' clause. + OpenMPAtClauseKind Kind = OMPC_AT_unknown; + + /// Start location of the kind in source code. + SourceLocation KindKwLoc; + + /// Set kind of the clause. + /// + /// \param K Kind of clause. + void setAtKind(OpenMPAtClauseKind K) { Kind = K; } + + /// Set clause kind location. + /// + /// \param KLoc Kind location. + void setAtKindKwLoc(SourceLocation KLoc) { KindKwLoc = KLoc; } + + /// Sets the location of '('. + void setLParenLoc(SourceLocation Loc) { LParenLoc = Loc; } + +public: + /// Build 'at' clause with argument \a A ('compilation' or 'execution'). + /// + /// \param A Argument of the clause ('compilation' or 'execution'). + /// \param ALoc Starting location of the argument. + /// \param StartLoc Starting location of the clause. + /// \param LParenLoc Location of '('. + /// \param EndLoc Ending location of the clause. + OMPAtClause(OpenMPAtClauseKind A, SourceLocation ALoc, + SourceLocation StartLoc, SourceLocation LParenLoc, + SourceLocation EndLoc) + : OMPClause(llvm::omp::OMPC_at, StartLoc, EndLoc), LParenLoc(LParenLoc), + Kind(A), KindKwLoc(ALoc) {} + + /// Build an empty clause. + OMPAtClause() + : OMPClause(llvm::omp::OMPC_at, SourceLocation(), SourceLocation()) {} + + /// Returns the locaiton of '('. + SourceLocation getLParenLoc() const { return LParenLoc; } + + /// Returns kind of the clause. + OpenMPAtClauseKind getAtKind() const { return Kind; } + + /// Returns location of clause kind. + SourceLocation getAtKindKwLoc() const { return KindKwLoc; } + + child_range children() { + return child_range(child_iterator(), child_iterator()); + } + + const_child_range children() const { + return const_child_range(const_child_iterator(), const_child_iterator()); + } + + child_range used_children() { + return child_range(child_iterator(), child_iterator()); + } + const_child_range used_children() const { + return const_child_range(const_child_iterator(), const_child_iterator()); + } + + static bool classof(const OMPClause *T) { + return T->getClauseKind() == llvm::omp::OMPC_at; + } +}; + /// This represents 'schedule' clause in the '#pragma omp ...' directive. /// /// \code diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -3310,6 +3310,11 @@ return true; } +template +bool RecursiveASTVisitor::VisitOMPAtClause(OMPAtClause *) { + return true; +} + template bool RecursiveASTVisitor::VisitOMPScheduleClause(OMPScheduleClause *C) { diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1401,6 +1401,8 @@ "expected declarator on 'omp declare mapper' directive">; def err_omp_unexpected_append_op : Error< "unexpected operation specified in 'append_args' clause, expected 'interop'">; +def err_omp_unexpected_execution_modifier : Error< + "unexpected 'execution' modifier in non-executable context">; def err_omp_declare_variant_wrong_clause : Error< "expected %select{'match'|'match', 'adjust_args', or 'append_args'}0 clause " "on 'omp declare variant' directive">; diff --git a/clang/include/clang/Basic/OpenMPKinds.h b/clang/include/clang/Basic/OpenMPKinds.h --- a/clang/include/clang/Basic/OpenMPKinds.h +++ b/clang/include/clang/Basic/OpenMPKinds.h @@ -131,6 +131,13 @@ OMPC_ATOMIC_DEFAULT_MEM_ORDER_unknown }; +/// OpenMP attributes for 'at' clause. +enum OpenMPAtClauseKind { +#define OPENMP_AT_KIND(Name) OMPC_AT_##Name, +#include "clang/Basic/OpenMPKinds.def" + OMPC_AT_unknown +}; + /// OpenMP device type for 'device_type' clause. enum OpenMPDeviceType { #define OPENMP_DEVICE_TYPE_KIND(Name) \ diff --git a/clang/include/clang/Basic/OpenMPKinds.def b/clang/include/clang/Basic/OpenMPKinds.def --- a/clang/include/clang/Basic/OpenMPKinds.def +++ b/clang/include/clang/Basic/OpenMPKinds.def @@ -41,6 +41,9 @@ #ifndef OPENMP_ATOMIC_DEFAULT_MEM_ORDER_KIND #define OPENMP_ATOMIC_DEFAULT_MEM_ORDER_KIND(Name) #endif +#ifndef OPENMP_AT_KIND +#define OPENMP_AT_KIND(Name) +#endif #ifndef OPENMP_DEFAULTMAP_MODIFIER #define OPENMP_DEFAULTMAP_MODIFIER(Name) #endif @@ -119,6 +122,10 @@ OPENMP_ATOMIC_DEFAULT_MEM_ORDER_KIND(acq_rel) OPENMP_ATOMIC_DEFAULT_MEM_ORDER_KIND(relaxed) +// Modifiers for 'at' clause. +OPENMP_AT_KIND(compilation) +OPENMP_AT_KIND(execution) + // Map types for 'map' clause. OPENMP_MAP_KIND(alloc) OPENMP_MAP_KIND(to) @@ -179,6 +186,7 @@ #undef OPENMP_SCHEDULE_MODIFIER #undef OPENMP_SCHEDULE_KIND #undef OPENMP_ATOMIC_DEFAULT_MEM_ORDER_KIND +#undef OPENMP_AT_KIND #undef OPENMP_MAP_KIND #undef OPENMP_MAP_MODIFIER_KIND #undef OPENMP_MOTION_MODIFIER_KIND diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -3293,6 +3293,15 @@ /// Parse 'omp end assumes' directive. void ParseOpenMPEndAssumesDirective(SourceLocation Loc); + /// Parses clauses for directive. + /// + /// \param DKind Kind of current directive. + /// \param clauses for current directive. + /// \param start location for clauses of current directive + void ParseOpenMPClauses(OpenMPDirectiveKind DKind, + SmallVector *Clauses, + SourceLocation Loc); + /// Parse clauses for '#pragma omp [begin] declare target'. void ParseOMPDeclareTargetClauses(Sema::DeclareTargetContextInfo &DTCI); diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -11826,6 +11826,13 @@ OpenMPAtomicDefaultMemOrderClauseKind Kind, SourceLocation KindLoc, SourceLocation StartLoc, SourceLocation LParenLoc, SourceLocation EndLoc); + /// Called on well-formed 'at' clause. + OMPClause *ActOnOpenMPAtClause(OpenMPAtClauseKind Kind, + SourceLocation KindLoc, + SourceLocation StartLoc, + SourceLocation LParenLoc, + SourceLocation EndLoc); + /// Data used for processing a list of variables in OpenMP clauses. struct OpenMPVarListDataTy final { Expr *DepModOrTailExpr = nullptr; diff --git a/clang/lib/AST/OpenMPClause.cpp b/clang/lib/AST/OpenMPClause.cpp --- a/clang/lib/AST/OpenMPClause.cpp +++ b/clang/lib/AST/OpenMPClause.cpp @@ -152,6 +152,7 @@ case OMPC_reverse_offload: case OMPC_dynamic_allocators: case OMPC_atomic_default_mem_order: + case OMPC_at: case OMPC_device_type: case OMPC_match: case OMPC_nontemporal: @@ -251,6 +252,7 @@ case OMPC_reverse_offload: case OMPC_dynamic_allocators: case OMPC_atomic_default_mem_order: + case OMPC_at: case OMPC_device_type: case OMPC_match: case OMPC_nontemporal: @@ -1781,6 +1783,11 @@ << ")"; } +void OMPClausePrinter::VisitOMPAtClause(OMPAtClause *Node) { + OS << "at(" << getOpenMPSimpleClauseTypeName(OMPC_at, Node->getAtKind()) + << ")"; +} + void OMPClausePrinter::VisitOMPScheduleClause(OMPScheduleClause *Node) { OS << "schedule("; if (Node->getFirstScheduleModifier() != OMPC_SCHEDULE_MODIFIER_unknown) { diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -530,6 +530,8 @@ void OMPClauseProfiler::VisitOMPAtomicDefaultMemOrderClause( const OMPAtomicDefaultMemOrderClause *C) {} +void OMPClauseProfiler::VisitOMPAtClause(const OMPAtClause *C) {} + void OMPClauseProfiler::VisitOMPScheduleClause(const OMPScheduleClause *C) { VistOMPClauseWithPreInit(C); if (auto *S = C->getChunkSize()) diff --git a/clang/lib/Basic/OpenMPKinds.cpp b/clang/lib/Basic/OpenMPKinds.cpp --- a/clang/lib/Basic/OpenMPKinds.cpp +++ b/clang/lib/Basic/OpenMPKinds.cpp @@ -99,6 +99,11 @@ .Case(#Name, OMPC_ATOMIC_DEFAULT_MEM_ORDER_##Name) #include "clang/Basic/OpenMPKinds.def" .Default(OMPC_ATOMIC_DEFAULT_MEM_ORDER_unknown); + case OMPC_at: + return llvm::StringSwitch(Str) +#define OPENMP_AT_KIND(Name) .Case(#Name, OMPC_AT_##Name) +#include "clang/Basic/OpenMPKinds.def" + .Default(OMPC_AT_unknown); case OMPC_device_type: return llvm::StringSwitch(Str) #define OPENMP_DEVICE_TYPE_KIND(Name) .Case(#Name, OMPC_DEVICE_TYPE_##Name) @@ -326,6 +331,16 @@ #include "clang/Basic/OpenMPKinds.def" } llvm_unreachable("Invalid OpenMP 'atomic_default_mem_order' clause type"); + case OMPC_at: + switch (Type) { + case OMPC_AT_unknown: + return "unknown"; +#define OPENMP_AT_KIND(Name) \ + case OMPC_AT_##Name: \ + return #Name; +#include "clang/Basic/OpenMPKinds.def" + } + llvm_unreachable("Invalid OpenMP 'at' clause type"); case OMPC_device_type: switch (Type) { case OMPC_DEVICE_TYPE_unknown: diff --git a/clang/lib/CodeGen/CGStmtOpenMP.cpp b/clang/lib/CodeGen/CGStmtOpenMP.cpp --- a/clang/lib/CodeGen/CGStmtOpenMP.cpp +++ b/clang/lib/CodeGen/CGStmtOpenMP.cpp @@ -6512,6 +6512,7 @@ case OMPC_reverse_offload: case OMPC_dynamic_allocators: case OMPC_atomic_default_mem_order: + case OMPC_at: case OMPC_device_type: case OMPC_match: case OMPC_nontemporal: diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -1627,6 +1627,45 @@ return false; } +/// [clause[ [,] clause] ... ] +/// +/// clauses: for error directive +/// 'at' '(' compilation | execution ')' +/// 'severity' '(' fatal | warning ')' +/// 'message' '(' msg-string ')' +/// .... +void Parser::ParseOpenMPClauses(OpenMPDirectiveKind DKind, + SmallVector *Clauses, + SourceLocation Loc) { + SmallVector, + llvm::omp::Clause_enumSize + 1> + FirstClauses(llvm::omp::Clause_enumSize + 1); + while (Tok.isNot(tok::annot_pragma_openmp_end)) { + OpenMPClauseKind CKind = Tok.isAnnotation() + ? OMPC_unknown + : getOpenMPClauseKind(PP.getSpelling(Tok)); + Actions.StartOpenMPClause(CKind); + OMPClause *Clause = ParseOpenMPClause( + DKind, CKind, !FirstClauses[unsigned(CKind)].getInt()); + SkipUntil(tok::comma, tok::identifier, tok::annot_pragma_openmp_end, + StopBeforeMatch); + FirstClauses[unsigned(CKind)].setInt(true); + if (Clause != nullptr) + Clauses->push_back(Clause); + OMPAtClause *AtC = CKind == OMPC_at ? cast(Clause) : nullptr; + if (CKind == OMPC_at && AtC->getAtKind() == OMPC_AT_execution) + Diag(AtC->getAtKindKwLoc(), diag::err_omp_unexpected_execution_modifier); + if (Tok.is(tok::annot_pragma_openmp_end)) { + Actions.EndOpenMPClause(); + break; + } + // Skip ',' if any. + if (Tok.is(tok::comma)) + ConsumeToken(); + Actions.EndOpenMPClause(); + } +} + /// `omp assumes` or `omp begin/end assumes` [[,]]... /// where /// @@ -2124,6 +2163,13 @@ ConsumeAnnotationToken(); return Actions.ActOnOpenMPRequiresDirective(StartLoc, Clauses); } + case OMPD_error: { + SmallVector Clauses; + SourceLocation StartLoc = ConsumeToken(); + ParseOpenMPClauses(DKind, &Clauses, StartLoc); + Actions.ActOnOpenMPErrorDirective(Clauses, StartLoc, SourceLocation()); + break; + } case OMPD_assumes: case OMPD_begin_assumes: ParseOpenMPAssumesDirective(DKind, ConsumeToken()); @@ -2310,7 +2356,6 @@ case OMPD_unroll: case OMPD_task: case OMPD_taskyield: - case OMPD_error: case OMPD_barrier: case OMPD_taskwait: case OMPD_taskgroup: @@ -2711,6 +2756,10 @@ ParsedStmtContext()) { Diag(Tok, diag::err_omp_immediate_directive) << getOpenMPDirectiveName(DKind) << 0; + if (DKind == OMPD_error) { + SkipUntil(tok::annot_pragma_openmp_end); + break; + } } HasAssociatedStatement = false; // Fall through for further analysis. @@ -3170,6 +3219,7 @@ case OMPC_default: case OMPC_proc_bind: case OMPC_atomic_default_mem_order: + case OMPC_at: case OMPC_order: case OMPC_bind: // OpenMP [2.14.3.1, Restrictions] @@ -3180,6 +3230,8 @@ // OpenMP [5.0, Requires directive, Restrictions] // At most one atomic_default_mem_order clause can appear // on the directive + // OpenMP [5.1, Requires directive, Restrictions] + // At most one at clause can appear on the directive // OpenMP 5.1, 2.11.7 loop Construct, Restrictions. // At most one bind clause can appear on a loop directive. if (!FirstClause && CKind != OMPC_order) { diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -6308,7 +6308,7 @@ break; case OMPD_error: assert(AStmt == nullptr && - "No associated statement allowed for 'omp taskyield' directive"); + "No associated statement allowed for 'omp error' directive"); Res = ActOnOpenMPErrorDirective(ClausesWithImplicit, StartLoc, EndLoc); break; case OMPD_barrier: @@ -6719,6 +6719,7 @@ case OMPC_device_type: case OMPC_match: case OMPC_when: + case OMPC_at: default: llvm_unreachable("Unexpected clause"); } @@ -11029,6 +11030,15 @@ StmtResult Sema::ActOnOpenMPErrorDirective(ArrayRef Clauses, SourceLocation StartLoc, SourceLocation EndLoc) { + const OMPAtClause *AtC = nullptr; + for (auto *AC : + OMPExecutableDirective::getClausesOfKind(Clauses)) + AtC = AC; + if (!AtC || AtC->getAtKind() == OMPC_AT_compilation) { + Diag(AtC ? AtC->getBeginLoc() : StartLoc, diag::err_diagnose_if_succeeded) + << "ERROR"; + return StmtError(); + } return OMPErrorDirective::Create(Context, StartLoc, EndLoc, Clauses); } @@ -15171,6 +15181,7 @@ case OMPC_match: case OMPC_nontemporal: case OMPC_order: + case OMPC_at: case OMPC_destroy: case OMPC_inclusive: case OMPC_exclusive: @@ -16096,6 +16107,7 @@ case OMPC_match: case OMPC_nontemporal: case OMPC_order: + case OMPC_at: case OMPC_destroy: case OMPC_detach: case OMPC_inclusive: @@ -16498,6 +16510,10 @@ Res = ActOnOpenMPBindClause(static_cast(Argument), ArgumentLoc, StartLoc, LParenLoc, EndLoc); break; + case OMPC_at: + Res = ActOnOpenMPAtClause(static_cast(Argument), + ArgumentLoc, StartLoc, LParenLoc, EndLoc); + break; case OMPC_if: case OMPC_final: case OMPC_num_threads: @@ -16676,6 +16692,22 @@ LParenLoc, EndLoc); } +OMPClause *Sema::ActOnOpenMPAtClause(OpenMPAtClauseKind Kind, + SourceLocation KindKwLoc, + SourceLocation StartLoc, + SourceLocation LParenLoc, + SourceLocation EndLoc) { + if (Kind == OMPC_AT_unknown) { + Diag(KindKwLoc, diag::err_omp_unexpected_clause_value) + << getListOfPossibleValues(OMPC_at, /*First=*/0, + /*Last=*/OMPC_AT_unknown) + << getOpenMPClauseName(OMPC_at); + return nullptr; + } + return new (Context) + OMPAtClause(Kind, KindKwLoc, StartLoc, LParenLoc, EndLoc); +} + OMPClause *Sema::ActOnOpenMPOrderClause(OpenMPOrderClauseKind Kind, SourceLocation KindKwLoc, SourceLocation StartLoc, @@ -16875,6 +16907,7 @@ case OMPC_match: case OMPC_nontemporal: case OMPC_order: + case OMPC_at: case OMPC_destroy: case OMPC_novariants: case OMPC_nocontext: @@ -17131,6 +17164,7 @@ case OMPC_match: case OMPC_nontemporal: case OMPC_order: + case OMPC_at: case OMPC_novariants: case OMPC_nocontext: case OMPC_detach: @@ -17685,6 +17719,7 @@ case OMPC_device_type: case OMPC_match: case OMPC_order: + case OMPC_at: case OMPC_destroy: case OMPC_novariants: case OMPC_nocontext: diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -2351,6 +2351,17 @@ return getSema().ActOnOpenMPAlignClause(A, StartLoc, LParenLoc, EndLoc); } + /// Build a new OpenMP 'at' clause. + /// + /// By default, performs semantic analysis to build the new OpenMP clause. + /// Subclasses may override this routine to provide different behavior. + OMPClause *RebuildOMPAtClause(OpenMPAtClauseKind Kind, SourceLocation KwLoc, + SourceLocation StartLoc, + SourceLocation LParenLoc, + SourceLocation EndLoc) { + return getSema().ActOnOpenMPAtClause(Kind, KwLoc, StartLoc, LParenLoc, + EndLoc); + } /// Rebuild the operand to an Objective-C \@synchronized statement. /// /// By default, performs semantic analysis to build the new statement. @@ -9855,6 +9866,13 @@ "atomic_default_mem_order clause cannot appear in dependent context"); } +template +OMPClause *TreeTransform::TransformOMPAtClause(OMPAtClause *C) { + return getDerived().RebuildOMPAtClause(C->getAtKind(), C->getAtKindKwLoc(), + C->getBeginLoc(), C->getLParenLoc(), + C->getEndLoc()); +} + template OMPClause * TreeTransform::TransformOMPPrivateClause(OMPPrivateClause *C) { diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -9934,7 +9934,10 @@ case llvm::omp::OMPC_atomic_default_mem_order: C = new (Context) OMPAtomicDefaultMemOrderClause(); break; - case llvm::omp::OMPC_private: + case llvm::omp::OMPC_at: + C = new (Context) OMPAtClause(); + break; + case llvm::omp::OMPC_private: C = OMPPrivateClause::CreateEmpty(Context, Record.readInt()); break; case llvm::omp::OMPC_firstprivate: @@ -10336,6 +10339,12 @@ C->setAtomicDefaultMemOrderKindKwLoc(Record.readSourceLocation()); } +void OMPClauseReader::VisitOMPAtClause(OMPAtClause *C) { + C->setAtKind(static_cast(Record.readInt())); + C->setLParenLoc(Record.readSourceLocation()); + C->setAtKindKwLoc(Record.readSourceLocation()); +} + void OMPClauseReader::VisitOMPPrivateClause(OMPPrivateClause *C) { C->setLParenLoc(Record.readSourceLocation()); unsigned NumVars = C->varlist_size(); diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -6982,6 +6982,12 @@ Record.AddSourceLocation(C->getAtomicDefaultMemOrderKindKwLoc()); } +void OMPClauseWriter::VisitOMPAtClause(OMPAtClause *C) { + Record.push_back(C->getAtKind()); + Record.AddSourceLocation(C->getLParenLoc()); + Record.AddSourceLocation(C->getAtKindKwLoc()); +} + void OMPClauseWriter::VisitOMPNontemporalClause(OMPNontemporalClause *C) { Record.push_back(C->varlist_size()); Record.AddSourceLocation(C->getLParenLoc()); diff --git a/clang/test/OpenMP/error_ast_print.cpp b/clang/test/OpenMP/error_ast_print.cpp --- a/clang/test/OpenMP/error_ast_print.cpp +++ b/clang/test/OpenMP/error_ast_print.cpp @@ -13,16 +13,16 @@ void foo() {} // CHECK: template int tmain(T argc, char **argv) // CHECK: static int a; -// CHECK-NEXT: #pragma omp error +// CHECK-NEXT: #pragma omp error at(execution) // CHECK-NEXT: a = argv[0][0]; // CHECK-NEXT: ++a; -// CHECK-NEXT: #pragma omp error +// CHECK-NEXT: #pragma omp error at(execution) // CHECK-NEXT: { // CHECK-NEXT: int b = 10; // CHECK-NEXT: T c = 100; // CHECK-NEXT: a = b + c; // CHECK-NEXT: } -// CHECK-NEXT: #pragma omp error +// CHECK-NEXT: #pragma omp error at(execution) // CHECK-NEXT: foo(); // CHECK-NEXT: return N; @@ -30,16 +30,16 @@ int tmain(T argc, char **argv) { T b = argc, c, d, e, f, g; static int a; -#pragma omp error +#pragma omp error at(execution) a = argv[0][0]; ++a; -#pragma omp error +#pragma omp error at(execution) { int b = 10; T c = 100; a = b + c; } -#pragma omp error +#pragma omp error at(execution) foo(); return N; } @@ -47,16 +47,16 @@ // CHECK: int main(int argc, char **argv) // CHECK-NEXT: int b = argc, c, d, e, f, g; // CHECK-NEXT: static int a; -// CHECK-NEXT: #pragma omp error +// CHECK-NEXT: #pragma omp error at(execution) // CHECK-NEXT: a = 2; -// CHECK-NEXT: #pragma omp error +// CHECK-NEXT: #pragma omp error at(execution) // CHECK-NEXT: foo(); int main (int argc, char **argv) { int b = argc, c, d, e, f, g; static int a; -#pragma omp error +#pragma omp error at(execution) a=2; -#pragma omp error +#pragma omp error at(execution) foo(); } #endif diff --git a/clang/test/OpenMP/error_message.cpp b/clang/test/OpenMP/error_message.cpp --- a/clang/test/OpenMP/error_message.cpp +++ b/clang/test/OpenMP/error_message.cpp @@ -7,19 +7,19 @@ if (argc) #pragma omp error // expected-error {{'#pragma omp error' cannot be an immediate substatement}} if (argc) { -#pragma omp error +#pragma omp error // expected-error {{ERROR}} } while (argc) #pragma omp error // expected-error {{'#pragma omp error' cannot be an immediate substatement}} while (argc) { -#pragma omp error +#pragma omp error // expected-error {{ERROR}} } do #pragma omp error // expected-error {{'#pragma omp error' cannot be an immediate substatement}} while (argc) ; do { -#pragma omp error +#pragma omp error // expected-error {{ERROR}} } while (argc); switch (argc) #pragma omp error // expected-error {{'#pragma omp error' cannot be an immediate substatement}} @@ -28,47 +28,75 @@ #pragma omp error // expected-error {{'#pragma omp error' cannot be an immediate substatement}} switch (argc) case 1: { -#pragma omp error +#pragma omp error // expected-error {{ERROR}} } switch (argc) { -#pragma omp error +#pragma omp error // expected-error {{ERROR}} case 1: -#pragma omp error +#pragma omp error // expected-error {{ERROR}} break; default: { -#pragma omp error +#pragma omp error // expected-error {{ERROR}} } break; } for (;;) #pragma omp error // expected-error {{'#pragma omp error' cannot be an immediate substatement}} for (;;) { -#pragma omp error +#pragma omp error // expected-error {{ERROR}} } label: -#pragma omp error +#pragma omp error // expected-error {{ERROR}} label1 : { -#pragma omp error +#pragma omp error // expected-error {{ERROR}} } if (1) label2: #pragma omp error // expected-error {{'#pragma omp error' cannot be an immediate substatement}} +// expected-error@+1 {{ERROR}} +#pragma omp error at() // expected-error {{expected 'compilation' or 'execution' in OpenMP clause 'at'}} + +// expected-error@+1 {{ERROR}} +#pragma omp error at(up) // expected-error {{expected 'compilation' or 'execution' in OpenMP clause 'at'}} + +// expected-error@+3 {{ERROR}} +// expected-error@+2 {{expected ')'}} +// expected-note@+1 {{to match this '('}} +#pragma omp error at(up(a)) // expected-error {{expected 'compilation' or 'execution' in OpenMP clause 'at'}} + +#pragma omp error at(execution) // no error + +#pragma omp error at(compilation) // expected-error {{ERROR}} return T(); } +#pragma omp error at(execution) // expected-error {{unexpected 'execution' modifier in non-executable context}} + +#pragma omp error at(compilation) // expected-error {{ERROR}} +class A { + +#pragma omp error at(compilation) // expected-error {{ERROR}} + +#pragma omp error at(execution) // expected-error {{unexpected 'execution' modifier in non-executable context}} + int A; +}; + int main(int argc, char **argv) { +// expected-error@+1 {{ERROR}} #pragma omp error ; +// expected-error@+1 {{ERROR}} #pragma omp error untied // expected-error {{unexpected OpenMP clause 'untied' in directive '#pragma omp error'}} -#pragma omp error unknown // expected-warning {{extra tokens at the end of '#pragma omp error' are ignored}} if (argc) #pragma omp error // expected-error {{'#pragma omp error' cannot be an immediate substatement}} if (argc) { +// expected-error@+1 {{ERROR}} #pragma omp error } while (argc) #pragma omp error // expected-error {{'#pragma omp error' cannot be an immediate substatement}} while (argc) { +// expected-error@+1 {{ERROR}} #pragma omp error } do @@ -76,6 +104,7 @@ while (argc) ; do { +// expected-error@+1 {{ERROR}} #pragma omp error } while (argc); switch (argc) @@ -85,25 +114,32 @@ #pragma omp error // expected-error {{'#pragma omp error' cannot be an immediate substatement}} switch (argc) case 1: { +// expected-error@+1 {{ERROR}} #pragma omp error } switch (argc) { +// expected-error@+1 {{ERROR}} #pragma omp error case 1: +// expected-error@+1 {{ERROR}} #pragma omp error break; default: { +// expected-error@+1 {{ERROR}} #pragma omp error } break; } for (;;) #pragma omp error // expected-error {{'#pragma omp error' cannot be an immediate substatement}} for (;;) { +// expected-error@+1 {{ERROR}} #pragma omp error } label: +// expected-error@+1 {{ERROR}} #pragma omp error label1 : { +// expected-error@+1 {{ERROR}} #pragma omp error } if (1) diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -2443,6 +2443,8 @@ void OMPClauseEnqueue::VisitOMPAtomicDefaultMemOrderClause( const OMPAtomicDefaultMemOrderClause *) {} +void OMPClauseEnqueue::VisitOMPAtClause(const OMPAtClause *) {} + void OMPClauseEnqueue::VisitOMPDeviceClause(const OMPDeviceClause *C) { Visitor->AddStmt(C->getDevice()); } 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 @@ -1868,6 +1868,7 @@ CHECK_SIMPLE_CLAUSE(Use, OMPC_use) CHECK_SIMPLE_CLAUSE(Novariants, OMPC_novariants) CHECK_SIMPLE_CLAUSE(Nocontext, OMPC_nocontext) +CHECK_SIMPLE_CLAUSE(At, OMPC_at) CHECK_SIMPLE_CLAUSE(Filter, OMPC_filter) CHECK_SIMPLE_CLAUSE(When, OMPC_when) CHECK_SIMPLE_CLAUSE(AdjustArgs, OMPC_adjust_args) diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td --- a/llvm/include/llvm/Frontend/OpenMP/OMP.td +++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td @@ -301,6 +301,9 @@ let clangClass = "OMPAtomicDefaultMemOrderClause"; let flangClass = "OmpAtomicDefaultMemOrderClause"; } +def OMPC_At : Clause<"at"> { + let clangClass = "OMPAtClause"; +} def OMPC_Allocate : Clause<"allocate"> { let clangClass = "OMPAllocateClause"; let flangClass = "OmpAllocateClause"; @@ -527,7 +530,11 @@ } def OMP_TaskYield : Directive<"taskyield"> {} def OMP_Barrier : Directive<"barrier"> {} -def OMP_Error : Directive<"error"> {} +def OMP_Error : Directive<"error"> { + let allowedClauses = [ + VersionedClause + ]; +} def OMP_TaskWait : Directive<"taskwait"> { let allowedClauses = [ VersionedClause