https://github.com/kevinsala created 
https://github.com/llvm/llvm-project/pull/208353

Add parsing for `dims` modifier (OpenMP 6.1) in `num_threads` clauses. Examples:

```cpp
constexpr int N = 2;
#pragma omp parallel num_threads(dims(N),strict: a, b)
{ ... }
```

>From d8ab549e3469184d66428a98d3d92c756cafc974 Mon Sep 17 00:00:00 2001
From: Kevin Sala <[email protected]>
Date: Wed, 8 Jul 2026 13:14:49 -0700
Subject: [PATCH] [Clang][OpenMP] Add parsing/sema for dims modifier in
 num_threads

---
 clang/include/clang/AST/OpenMPClause.h        | 184 ++++++++++++++----
 clang/include/clang/AST/RecursiveASTVisitor.h |   4 +-
 clang/include/clang/Basic/OpenMPKinds.def     |   3 +-
 clang/include/clang/Sema/SemaOpenMP.h         |   9 +-
 clang/lib/AST/OpenMPClause.cpp                |  59 +++++-
 clang/lib/AST/StmtProfile.cpp                 |   7 +-
 clang/lib/CodeGen/CGExprScalar.cpp            |   1 +
 clang/lib/CodeGen/CGOpenMPRuntime.cpp         |   6 +-
 clang/lib/CodeGen/CGStmtOpenMP.cpp            |   6 +-
 clang/lib/Parse/ParseOpenMP.cpp               | 113 +++++++----
 clang/lib/Sema/SemaOpenMP.cpp                 |  91 +++++----
 clang/lib/Sema/TreeTransform.h                |  41 ++--
 clang/lib/Serialization/ASTReader.cpp         |  17 +-
 clang/lib/Serialization/ASTWriter.cpp         |  11 +-
 clang/test/OpenMP/dims_modifier_ast_print.cpp |  62 ++++++
 clang/test/OpenMP/dims_modifier_messages.cpp  |  54 ++++-
 ...bute_parallel_for_num_threads_messages.cpp |   4 +-
 ...parallel_for_simd_num_threads_messages.cpp |   4 +-
 .../parallel_for_num_threads_messages.cpp     |   4 +-
 ...parallel_for_simd_num_threads_messages.cpp |   4 +-
 .../parallel_masked_num_threads_messages.cpp  |   4 +-
 .../parallel_master_num_threads_messages.cpp  |   4 +-
 .../OpenMP/parallel_num_threads_messages.cpp  |  28 +--
 ...parallel_sections_num_threads_messages.cpp |   4 +-
 ...rget_parallel_for_num_threads_messages.cpp |   4 +-
 ...parallel_for_simd_num_threads_messages.cpp |   4 +-
 .../target_parallel_num_threads_messages.cpp  |  28 +--
 ...bute_parallel_for_num_threads_messages.cpp |   4 +-
 ...parallel_for_simd_num_threads_messages.cpp |   4 +-
 ...parallel_for_simd_num_threads_messages.cpp |   4 +-
 clang/tools/libclang/CIndex.cpp               |   4 +-
 31 files changed, 578 insertions(+), 198 deletions(-)

diff --git a/clang/include/clang/AST/OpenMPClause.h 
b/clang/include/clang/AST/OpenMPClause.h
index 641c03bf8ff5c..a3048709ccd8c 100644
--- a/clang/include/clang/AST/OpenMPClause.h
+++ b/clang/include/clang/AST/OpenMPClause.h
@@ -804,67 +804,175 @@ class OMPFinalClause final
     return const_cast<OMPFinalClause *>(this)->used_children();
   }
 };
+
 /// This represents 'num_threads' clause in the '#pragma omp ...'
 /// directive.
 ///
 /// \code
-/// #pragma omp parallel num_threads(6)
+/// #pragma omp parallel num_threads(n)
+/// \endcode
+/// In this example directive '#pragma omp parallel' has 'num_threads' clause
+/// requesting 'n' threads.
+///
+/// \code
+/// #pragma omp parallel num_threads(strict: n)
 /// \endcode
-/// In this example directive '#pragma omp parallel' has simple 'num_threads'
-/// clause with number of threads '6'.
+/// In this example directive '#pragma omp parallel' has 'num_threads' clause
+/// requesting 'n' threads strictly with the 'strict' modifier.
+///
+/// \code
+/// #pragma omp parallel num_threads(dims(2): x, y)
+/// \endcode
+/// In this example directive '#pragma omp parallel' has clause 'num_threads'
+/// with the 'dims' modifier specifying two dimensions. The list specifies the
+/// number of threads in each dimension.
 class OMPNumThreadsClause final
-    : public OMPOneStmtClause<llvm::omp::OMPC_num_threads, OMPClause>,
-      public OMPClauseWithPreInit {
+    : public OMPVarListClause<OMPNumThreadsClause>,
+      public OMPClauseWithPreInit,
+      private llvm::TrailingObjects<OMPNumThreadsClause, Expr *> {
+  friend class OMPVarListClause<OMPNumThreadsClause>;
+  friend TrailingObjects;
   friend class OMPClauseReader;
 
-  /// Modifiers for 'num_threads' clause.
-  OpenMPNumThreadsClauseModifier Modifier = OMPC_NUMTHREADS_unknown;
+private:
+  /// Categories of modifiers for the 'num_threads' clause.
+  enum { PRESCRIPTIVENESS, DIMS, NUM_MODIFIERS };
 
-  /// Location of the modifier.
-  SourceLocation ModifierLoc;
+  /// Modifiers for the clause, indexed by category.
+  OpenMPNumThreadsClauseModifier Modifiers[NUM_MODIFIERS] = {
+      OMPC_NUMTHREADS_unknown, OMPC_NUMTHREADS_unknown};
 
-  /// Sets modifier.
-  void setModifier(OpenMPNumThreadsClauseModifier M) { Modifier = M; }
+  /// Locations of the modifiers, indexed by category.
+  SourceLocation ModifiersLoc[NUM_MODIFIERS];
 
-  /// Sets modifier location.
-  void setModifierLoc(SourceLocation Loc) { ModifierLoc = Loc; }
+  /// Build clause with number of expressions \a N.
+  ///
+  /// \param C AST context.
+  /// \param StartLoc Starting location of the clause.
+  /// \param LParenLoc Location of '('.
+  /// \param EndLoc Ending location of the clause.
+  /// \param N Number of expressions.
+  OMPNumThreadsClause(const ASTContext &C, SourceLocation StartLoc,
+                      SourceLocation LParenLoc, SourceLocation EndLoc,
+                      unsigned N)
+      : OMPVarListClause<OMPNumThreadsClause>(llvm::omp::OMPC_num_threads,
+                                              StartLoc, LParenLoc, EndLoc, N),
+        OMPClauseWithPreInit(this) {}
 
-  /// Set condition.
-  void setNumThreads(Expr *NThreads) { setStmt(NThreads); }
+  /// Build an empty clause.
+  ///
+  /// \param N Number of expressions.
+  explicit OMPNumThreadsClause(unsigned N)
+      : OMPVarListClause<OMPNumThreadsClause>(
+            llvm::omp::OMPC_num_threads, SourceLocation(), SourceLocation(),
+            SourceLocation(), N),
+        OMPClauseWithPreInit(this) {}
+
+  /// Sets the dims modifier.
+  void setDimsModifier(OpenMPNumThreadsClauseModifier M) {
+    Modifiers[DIMS] = M;
+  }
+
+  /// Sets the prescriptiveness modifier.
+  void setPrescriptivenessModifier(OpenMPNumThreadsClauseModifier M) {
+    Modifiers[PRESCRIPTIVENESS] = M;
+  }
+
+  /// Sets the location of the prescriptiveness modifier.
+  void setPrescriptivenessModifierLoc(SourceLocation Loc) {
+    ModifiersLoc[PRESCRIPTIVENESS] = Loc;
+  }
+
+  /// Sets the location of the dims modifier.
+  void setDimsModifierLoc(SourceLocation Loc) { ModifiersLoc[DIMS] = Loc; }
+
+  /// Sets the dims modifier expression.
+  void setDimsModifierExpr(Expr *E) { *varlist_end() = E; }
 
 public:
-  /// Build 'num_threads' clause with condition \a NumThreads.
+  /// Creates clause with a list of variables/expressions.
   ///
-  /// \param Modifier Clause modifier.
-  /// \param NumThreads Number of threads for the construct.
-  /// \param HelperNumThreads Helper Number of threads for the construct.
-  /// \param CaptureRegion Innermost OpenMP region where expressions in this
-  /// clause must be captured.
+  /// \param C AST context.
+  /// \param CaptureRegion The captured region for the pre-init statements.
   /// \param StartLoc Starting location of the clause.
   /// \param LParenLoc Location of '('.
-  /// \param ModifierLoc Modifier location.
   /// \param EndLoc Ending location of the clause.
-  OMPNumThreadsClause(OpenMPNumThreadsClauseModifier Modifier, Expr 
*NumThreads,
-                      Stmt *HelperNumThreads, OpenMPDirectiveKind 
CaptureRegion,
-                      SourceLocation StartLoc, SourceLocation LParenLoc,
-                      SourceLocation ModifierLoc, SourceLocation EndLoc)
-      : OMPOneStmtClause(NumThreads, StartLoc, LParenLoc, EndLoc),
-        OMPClauseWithPreInit(this), Modifier(Modifier),
-        ModifierLoc(ModifierLoc) {
-    setPreInitStmt(HelperNumThreads, CaptureRegion);
+  /// \param VL List of references to the expressions.
+  /// \param PrescriptivenessModifier The prescriptiveness modifier.
+  /// \param DimsModifier The dims modifier.
+  /// \param PrescriptivenessModifierLoc Location of the prescriptiveness 
modifier.
+  /// \param DimsModifierLoc Location of the dims modifier.
+  /// \param DimsModifierExpr The expression of the number of dimensions.
+  /// \param PreInit
+  static OMPNumThreadsClause *
+  Create(const ASTContext &C, OpenMPDirectiveKind CaptureRegion,
+         SourceLocation StartLoc, SourceLocation LParenLoc,
+         SourceLocation EndLoc, ArrayRef<Expr *> VL,
+         OpenMPNumThreadsClauseModifier PrescriptivenessModifier,
+         OpenMPNumThreadsClauseModifier DimsModifier,
+         SourceLocation PrescriptivenessModifierLoc,
+         SourceLocation DimsModifierLoc, Expr *DimsModifierExpr, Stmt 
*PreInit);
+
+  /// Creates an empty clause with the place for \a N expressions.
+  ///
+  /// \param C AST context.
+  /// \param N The number of expressions.
+  static OMPNumThreadsClause *CreateEmpty(const ASTContext &C, unsigned N);
+
+  /// Return NumThreads expressions.
+  ArrayRef<Expr *> getNumThreads() { return getVarRefs(); }
+  ArrayRef<Expr *> getNumThreads() const {
+    return const_cast<OMPNumThreadsClause *>(this)->getNumThreads();
   }
 
-  /// Build an empty clause.
-  OMPNumThreadsClause() : OMPOneStmtClause(), OMPClauseWithPreInit(this) {}
+  /// Returns the prescriptiveness modifier.
+  OpenMPNumThreadsClauseModifier getPrescriptivenessModifier() const {
+    return Modifiers[PRESCRIPTIVENESS];
+  }
+  /// Returns the location of the prescriptiveness modifier.
+  SourceLocation getPrescriptivenessModifierLoc() const {
+    return ModifiersLoc[PRESCRIPTIVENESS];
+  }
 
-  /// Gets modifier.
-  OpenMPNumThreadsClauseModifier getModifier() const { return Modifier; }
+  /// Returns the dims modifier.
+  OpenMPNumThreadsClauseModifier getDimsModifier() const {
+    return Modifiers[DIMS];
+  }
+  /// Returns the location of the dims modifier.
+  SourceLocation getDimsModifierLoc() const { return ModifiersLoc[DIMS]; }
 
-  /// Gets modifier location.
-  SourceLocation getModifierLoc() const { return ModifierLoc; }
+  /// Checks if the clause has the dims modifier.
+  bool hasDimsModifier() const {
+    return Modifiers[DIMS] == OMPC_NUMTHREADS_dims;
+  }
 
-  /// Returns number of threads.
-  Expr *getNumThreads() const { return getStmtAs<Expr>(); }
+  /// Returns the dims modifier expression if present.
+  Expr *getDimsModifierExpr() {
+    return hasDimsModifier() ? *varlist_end() : nullptr;
+  }
+  /// Returns the dims modifier expression if present.
+  const Expr *getDimsModifierExpr() const {
+    return const_cast<OMPNumThreadsClause *>(this)->getDimsModifierExpr();
+  }
+
+  child_range children() {
+    return child_range(reinterpret_cast<Stmt **>(varlist_begin()),
+                       reinterpret_cast<Stmt **>(varlist_end()) + 1);
+  }
+  const_child_range children() const {
+    return const_cast<OMPNumThreadsClause *>(this)->children();
+  }
+
+  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_num_threads;
+  }
 };
 
 /// This represents 'safelen' clause in the '#pragma omp ...'
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h 
b/clang/include/clang/AST/RecursiveASTVisitor.h
index 84aaac71746ed..1c0c182241064 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -3501,8 +3501,10 @@ bool 
RecursiveASTVisitor<Derived>::VisitOMPFinalClause(OMPFinalClause *C) {
 template <typename Derived>
 bool
 RecursiveASTVisitor<Derived>::VisitOMPNumThreadsClause(OMPNumThreadsClause *C) 
{
+  if (auto *E = C->getDimsModifierExpr())
+    TRY_TO(VisitStmt(E));
+  TRY_TO(VisitOMPClauseList(C));
   TRY_TO(VisitOMPClauseWithPreInit(C));
-  TRY_TO(TraverseStmt(C->getNumThreads()));
   return true;
 }
 
diff --git a/clang/include/clang/Basic/OpenMPKinds.def 
b/clang/include/clang/Basic/OpenMPKinds.def
index 5c396a5d2d4b3..5b5222a2ab446 100644
--- a/clang/include/clang/Basic/OpenMPKinds.def
+++ b/clang/include/clang/Basic/OpenMPKinds.def
@@ -278,8 +278,9 @@ OPENMP_NUMTASKS_MODIFIER(strict)
 OPENMP_NUMTEAMS_MODIFIER(lower_bound)
 OPENMP_NUMTEAMS_MODIFIER(dims)
 
-// Modifiers for the 'num_tasks' clause.
+// Modifiers for the 'num_threads' clause.
 OPENMP_NUMTHREADS_MODIFIER(strict)
+OPENMP_NUMTHREADS_MODIFIER(dims)
 
 // Modifiers for the 'thread_limit' clause.
 OPENMP_THREADLIMIT_MODIFIER(dims)
diff --git a/clang/include/clang/Sema/SemaOpenMP.h 
b/clang/include/clang/Sema/SemaOpenMP.h
index 9ef388a744db8..93f8bd7f85958 100644
--- a/clang/include/clang/Sema/SemaOpenMP.h
+++ b/clang/include/clang/Sema/SemaOpenMP.h
@@ -898,9 +898,12 @@ class SemaOpenMP : public SemaBase {
                                     SourceLocation EndLoc);
   /// Called on well-formed 'num_threads' clause.
   OMPClause *ActOnOpenMPNumThreadsClause(
-      OpenMPNumThreadsClauseModifier Modifier, Expr *NumThreads,
-      SourceLocation StartLoc, SourceLocation LParenLoc,
-      SourceLocation ModifierLoc, SourceLocation EndLoc);
+      ArrayRef<Expr *> VarList,
+      OpenMPNumThreadsClauseModifier PrescriptivenessModifier,
+      SourceLocation PrescriptivenessModifierLoc,
+      OpenMPNumThreadsClauseModifier DimsModifier, Expr *DimsModifierExpr,
+      SourceLocation DimsModifierLoc, SourceLocation StartLoc,
+      SourceLocation LParenLoc, SourceLocation EndLoc);
   /// Called on well-formed 'align' clause.
   OMPClause *ActOnOpenMPAlignClause(Expr *Alignment, SourceLocation StartLoc,
                                     SourceLocation LParenLoc,
diff --git a/clang/lib/AST/OpenMPClause.cpp b/clang/lib/AST/OpenMPClause.cpp
index d451255bf5845..2860a96d4e3e4 100644
--- a/clang/lib/AST/OpenMPClause.cpp
+++ b/clang/lib/AST/OpenMPClause.cpp
@@ -1999,6 +1999,35 @@ OMPThreadLimitClause 
*OMPThreadLimitClause::CreateEmpty(const ASTContext &C,
   return new (Mem) OMPThreadLimitClause(N);
 }
 
+OMPNumThreadsClause *OMPNumThreadsClause::Create(
+    const ASTContext &C, OpenMPDirectiveKind CaptureRegion,
+    SourceLocation StartLoc, SourceLocation LParenLoc, SourceLocation EndLoc,
+    ArrayRef<Expr *> VL,
+    OpenMPNumThreadsClauseModifier PrescriptivenessModifier,
+    OpenMPNumThreadsClauseModifier DimsModifier,
+    SourceLocation PrescriptivenessModifierLoc, SourceLocation DimsModifierLoc,
+    Expr *DimsModifierExpr, Stmt *PreInit) {
+  // Reserve space for an extra modifier expression.
+  void *Mem = C.Allocate(totalSizeToAlloc<Expr *>(VL.size() + 1));
+  OMPNumThreadsClause *Clause =
+      new (Mem) OMPNumThreadsClause(C, StartLoc, LParenLoc, EndLoc, VL.size());
+  Clause->setVarRefs(VL);
+  Clause->setPrescriptivenessModifier(PrescriptivenessModifier);
+  Clause->setPrescriptivenessModifierLoc(PrescriptivenessModifierLoc);
+  Clause->setDimsModifier(DimsModifier);
+  Clause->setDimsModifierExpr(DimsModifierExpr);
+  Clause->setDimsModifierLoc(DimsModifierLoc);
+  Clause->setPreInitStmt(PreInit, CaptureRegion);
+  return Clause;
+}
+
+OMPNumThreadsClause *OMPNumThreadsClause::CreateEmpty(const ASTContext &C,
+                                                      unsigned N) {
+  // Reserve space for an extra modifier expression.
+  void *Mem = C.Allocate(totalSizeToAlloc<Expr *>(N + 1));
+  return new (Mem) OMPNumThreadsClause(N);
+}
+
 
//===----------------------------------------------------------------------===//
 //  OpenMP clauses printing methods
 
//===----------------------------------------------------------------------===//
@@ -2018,14 +2047,30 @@ void 
OMPClausePrinter::VisitOMPFinalClause(OMPFinalClause *Node) {
 }
 
 void OMPClausePrinter::VisitOMPNumThreadsClause(OMPNumThreadsClause *Node) {
-  OS << "num_threads(";
-  OpenMPNumThreadsClauseModifier Modifier = Node->getModifier();
-  if (Modifier != OMPC_NUMTHREADS_unknown) {
-    OS << getOpenMPSimpleClauseTypeName(Node->getClauseKind(), Modifier)
-       << ": ";
+  if (!Node->varlist_empty()) {
+    OS << "num_threads";
+    bool HasPrescriptiveness =
+        Node->getPrescriptivenessModifier() != OMPC_NUMTHREADS_unknown;
+    bool HasDims = Node->getDimsModifier() != OMPC_NUMTHREADS_unknown;
+    if (HasPrescriptiveness || HasDims) {
+      OS << "(";
+      if (HasPrescriptiveness)
+        OS << getOpenMPSimpleClauseTypeName(
+            Node->getClauseKind(), Node->getPrescriptivenessModifier());
+      if (HasPrescriptiveness && HasDims)
+        OS << ",";
+      if (HasDims) {
+        OS << "dims(";
+        Node->getDimsModifierExpr()->printPretty(OS, nullptr, Policy, 0);
+        OS << ")";
+      }
+      OS << ":";
+      VisitOMPClauseList(Node, ' ');
+    } else {
+      VisitOMPClauseList(Node, '(');
+    }
+    OS << ")";
   }
-  Node->getNumThreads()->printPretty(OS, nullptr, Policy, 0);
-  OS << ")";
 }
 
 void OMPClausePrinter::VisitOMPAlignClause(OMPAlignClause *Node) {
diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
index 291c72385518e..0751783483bf6 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -475,9 +475,12 @@ void OMPClauseProfiler::VisitOMPFinalClause(const 
OMPFinalClause *C) {
 }
 
 void OMPClauseProfiler::VisitOMPNumThreadsClause(const OMPNumThreadsClause *C) 
{
+  Profiler->VisitInteger(C->getPrescriptivenessModifier());
+  Profiler->VisitInteger(C->getDimsModifier());
+  if (const Expr *Modifier = C->getDimsModifierExpr())
+    Profiler->VisitStmt(Modifier);
+  VisitOMPClauseList(C);
   VisitOMPClauseWithPreInit(C);
-  if (C->getNumThreads())
-    Profiler->VisitStmt(C->getNumThreads());
 }
 
 void OMPClauseProfiler::VisitOMPAlignClause(const OMPAlignClause *C) {
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp 
b/clang/lib/CodeGen/CGExprScalar.cpp
index 18ed6570730f4..f957eb4d859c9 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -6201,6 +6201,7 @@ Value *ScalarExprEmitter::VisitAtomicExpr(AtomicExpr *E) {
 /// Emit the computation of the specified expression of scalar type, ignoring
 /// the result.
 Value *CodeGenFunction::EmitScalarExpr(const Expr *E, bool IgnoreResultAssign) 
{
+  assert(E && "Invalid expression to emit");
   assert(E && hasScalarEvaluationKind(E->getType()) &&
          "Invalid scalar expression to emit");
 
diff --git a/clang/lib/CodeGen/CGOpenMPRuntime.cpp 
b/clang/lib/CodeGen/CGOpenMPRuntime.cpp
index eb2f92cdbf972..67d575d9d5e0d 100644
--- a/clang/lib/CodeGen/CGOpenMPRuntime.cpp
+++ b/clang/lib/CodeGen/CGOpenMPRuntime.cpp
@@ -6701,7 +6701,7 @@ static void getNumThreads(CodeGenFunction &CGF, const 
CapturedStmt *CS,
       CodeGenFunction::CGCapturedStmtRAII CapInfoRAII(CGF, &CGInfo);
       const auto *NumThreadsClause =
           Dir->getSingleClause<OMPNumThreadsClause>();
-      const Expr *NTExpr = NumThreadsClause->getNumThreads();
+      const Expr *NTExpr = NumThreadsClause->getNumThreads().front();
       if (NTExpr->isIntegerConstantExpr(CGF.getContext()))
         if (auto Constant = NTExpr->getIntegerConstantExpr(CGF.getContext()))
           UpperBound =
@@ -6887,8 +6887,8 @@ const Expr 
*CGOpenMPRuntime::getNumThreadsExprForTargetDirective(
     if (D.hasClausesOfKind<OMPNumThreadsClause>()) {
       CodeGenFunction::RunCleanupsScope NumThreadsScope(CGF);
       const auto *NumThreadsClause = D.getSingleClause<OMPNumThreadsClause>();
-      CheckForConstExpr(NumThreadsClause->getNumThreads(), nullptr);
-      return NumThreadsClause->getNumThreads();
+      CheckForConstExpr(NumThreadsClause->getNumThreads().front(), nullptr);
+      return NumThreadsClause->getNumThreads().front();
     }
     return NT;
   }
diff --git a/clang/lib/CodeGen/CGStmtOpenMP.cpp 
b/clang/lib/CodeGen/CGStmtOpenMP.cpp
index 88f698f38cce0..9174f6c63a751 100644
--- a/clang/lib/CodeGen/CGStmtOpenMP.cpp
+++ b/clang/lib/CodeGen/CGStmtOpenMP.cpp
@@ -1913,9 +1913,9 @@ static void emitCommonOMPParallelDirective(
 
   if (const auto *NumThreadsClause = S.getSingleClause<OMPNumThreadsClause>()) 
{
     CodeGenFunction::RunCleanupsScope NumThreadsScope(CGF);
-    NumThreads = CGF.EmitScalarExpr(NumThreadsClause->getNumThreads(),
+    NumThreads = CGF.EmitScalarExpr(NumThreadsClause->getNumThreads().front(),
                                     /*IgnoreResultAssign=*/true);
-    Modifier = NumThreadsClause->getModifier();
+    Modifier = NumThreadsClause->getPrescriptivenessModifier();
     if (const auto *MessageClause = S.getSingleClause<OMPMessageClause>()) {
       Message = MessageClause->getMessageString();
       MessageLoc = MessageClause->getBeginLoc();
@@ -2113,7 +2113,7 @@ void CodeGenFunction::EmitOMPParallelDirective(const 
OMPParallelDirective &S) {
 
     llvm::Value *NumThreads = nullptr;
     if (const auto *NumThreadsClause = 
S.getSingleClause<OMPNumThreadsClause>())
-      NumThreads = EmitScalarExpr(NumThreadsClause->getNumThreads(),
+      NumThreads = EmitScalarExpr(NumThreadsClause->getNumThreads().front(),
                                   /*IgnoreResultAssign=*/true);
 
     ProcBindKind ProcBind = OMP_PROC_BIND_default;
diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp
index 7ef1e4211a1ef..4e19082f8f814 100644
--- a/clang/lib/Parse/ParseOpenMP.cpp
+++ b/clang/lib/Parse/ParseOpenMP.cpp
@@ -3231,7 +3231,6 @@ OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind 
DKind,
 
   switch (CKind) {
   case OMPC_final:
-  case OMPC_num_threads:
   case OMPC_safelen:
   case OMPC_simdlen:
   case OMPC_collapse:
@@ -3297,7 +3296,7 @@ OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind 
DKind,
         PP.LookAhead(/*N=*/0).isNot(tok::l_paren))
       Clause = ParseOpenMPClause(CKind, WrongDirective);
     else if (CKind == OMPC_grainsize || CKind == OMPC_num_tasks ||
-             CKind == OMPC_num_threads || CKind == OMPC_dyn_groupprivate)
+             CKind == OMPC_dyn_groupprivate)
       Clause = ParseOpenMPSingleExprWithArgClause(DKind, CKind, 
WrongDirective);
     else
       Clause = ParseOpenMPSingleExprClause(CKind, WrongDirective);
@@ -3428,6 +3427,7 @@ OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind 
DKind,
     break;
   case OMPC_num_teams:
   case OMPC_thread_limit:
+  case OMPC_num_threads:
     if (!FirstClause) {
       Diag(Tok, diag::err_omp_more_one_clause)
           << getOpenMPDirectiveName(DKind, OMPVersion)
@@ -4331,33 +4331,6 @@ OMPClause 
*Parser::ParseOpenMPSingleExprWithArgClause(OpenMPDirectiveKind DKind,
       Arg.push_back(OMPC_NUMTASKS_unknown);
       KLoc.emplace_back();
     }
-  } else if (Kind == OMPC_num_threads) {
-    // Parse optional <num_threads modifier> ':'
-    OpenMPNumThreadsClauseModifier Modifier =
-        static_cast<OpenMPNumThreadsClauseModifier>(getOpenMPSimpleClauseType(
-            Kind, Tok.isAnnotation() ? "" : PP.getSpelling(Tok),
-            getLangOpts()));
-    if (getLangOpts().OpenMP >= 60) {
-      if (NextToken().is(tok::colon)) {
-        Arg.push_back(Modifier);
-        KLoc.push_back(Tok.getLocation());
-        // Parse modifier
-        ConsumeAnyToken();
-        // Parse ':'
-        ConsumeAnyToken();
-      } else {
-        if (Modifier == OMPC_NUMTHREADS_strict) {
-          Diag(Tok, diag::err_modifier_expected_colon) << "strict";
-          // Parse modifier
-          ConsumeAnyToken();
-        }
-        Arg.push_back(OMPC_NUMTHREADS_unknown);
-        KLoc.emplace_back();
-      }
-    } else {
-      Arg.push_back(OMPC_NUMTHREADS_unknown);
-      KLoc.emplace_back();
-    }
   } else {
     assert(Kind == OMPC_if);
     KLoc.push_back(Tok.getLocation());
@@ -4378,11 +4351,11 @@ OMPClause 
*Parser::ParseOpenMPSingleExprWithArgClause(OpenMPDirectiveKind DKind,
     }
   }
 
-  bool NeedAnExpression =
-      (Kind == OMPC_schedule && DelimLoc.isValid()) ||
-      (Kind == OMPC_dist_schedule && DelimLoc.isValid()) || Kind == OMPC_if ||
-      Kind == OMPC_device || Kind == OMPC_grainsize || Kind == OMPC_num_tasks 
||
-      Kind == OMPC_num_threads || Kind == OMPC_dyn_groupprivate;
+  bool NeedAnExpression = (Kind == OMPC_schedule && DelimLoc.isValid()) ||
+                          (Kind == OMPC_dist_schedule && DelimLoc.isValid()) ||
+                          Kind == OMPC_if || Kind == OMPC_device ||
+                          Kind == OMPC_grainsize || Kind == OMPC_num_tasks ||
+                          Kind == OMPC_dyn_groupprivate;
   if (NeedAnExpression) {
     SourceLocation ELoc = Tok.getLocation();
     ExprResult LHS(
@@ -5355,6 +5328,78 @@ bool Parser::ParseOpenMPVarList(OpenMPDirectiveKind 
DKind,
         TPA.Revert();
       }
     }
+  } else if (Kind == OMPC_num_threads) {
+    Data.ExtraModifierArray[0] = static_cast<int>(OMPC_NUMTHREADS_unknown);
+    Data.ExtraModifierArray[1] = static_cast<int>(OMPC_NUMTHREADS_unknown);
+
+    bool HasModifier = false;
+    while (true) {
+      if (Tok.is(tok::identifier) && Tok.getIdentifierInfo()->isStr("dims") &&
+          NextToken().is(tok::l_paren)) {
+
+        SourceLocation TLoc = Tok.getLocation();
+        ConsumeToken();
+        SourceLocation RLoc;
+
+        ExprResult ExprR =
+            ParseOpenMPParensExpr(getOpenMPClauseName(Kind), RLoc);
+
+        if (Data.ExtraModifierArray[1] != OMPC_NUMTHREADS_unknown) {
+          Diag(TLoc, diag::err_omp_duplicate_modifier)
+              << getOpenMPSimpleClauseTypeName(Kind, OMPC_NUMTHREADS_dims)
+              << getOpenMPClauseName(Kind);
+        } else if (ExprR.isUsable()) {
+          Data.ExtraModifierArray[1] = static_cast<int>(OMPC_NUMTHREADS_dims);
+          Data.ExtraModifierExprArray[1] = ExprR.get();
+          Data.ExtraModifierLocArray[1] = TLoc;
+        }
+        HasModifier = true;
+      } else {
+        StringRef ModName = Tok.isAnnotation() ? "" : PP.getSpelling(Tok);
+        OpenMPNumThreadsClauseModifier Modifier =
+            static_cast<OpenMPNumThreadsClauseModifier>(
+                getOpenMPSimpleClauseType(Kind, ModName, getLangOpts()));
+
+        // The dims modifier is a complex modifier. It must be parsed above.
+        if (Modifier == OMPC_NUMTHREADS_dims)
+          Modifier = OMPC_NUMTHREADS_unknown;
+
+        if (Modifier != OMPC_NUMTHREADS_unknown) {
+          if (Data.ExtraModifierArray[0] != OMPC_NUMTHREADS_unknown) {
+            Diag(Tok, diag::err_omp_duplicate_modifier)
+                << getOpenMPSimpleClauseTypeName(Kind, OMPC_NUMTHREADS_strict)
+                << getOpenMPClauseName(Kind);
+          } else {
+            Data.ExtraModifierArray[0] = Modifier;
+            Data.ExtraModifierLocArray[0] = Tok.getLocation();
+          }
+          ConsumeAnyToken();
+          HasModifier = true;
+        } else {
+          break;
+        }
+      }
+
+      if (Tok.is(tok::comma)) {
+        ConsumeToken();
+      } else {
+        break;
+      }
+    }
+
+    if (HasModifier) {
+      if (Tok.is(tok::colon)) {
+        ConsumeToken();
+      } else {
+        Diag(Tok, diag::err_modifier_expected_colon)
+            << getOpenMPClauseName(Kind);
+        SkipUntil(tok::r_paren, tok::annot_pragma_openmp_end, StopBeforeMatch);
+        Data.RLoc = Tok.getLocation();
+        if (!T.consumeClose())
+          Data.RLoc = T.getCloseLocation();
+        return true;
+      }
+    }
   }
 
   bool IsComma =
diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index f961c2887d9ed..1ba8644765ec8 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -17341,43 +17341,62 @@ static std::string 
getListOfPossibleValues(OpenMPClauseKind K, unsigned First,
 }
 
 OMPClause *SemaOpenMP::ActOnOpenMPNumThreadsClause(
-    OpenMPNumThreadsClauseModifier Modifier, Expr *NumThreads,
-    SourceLocation StartLoc, SourceLocation LParenLoc,
-    SourceLocation ModifierLoc, SourceLocation EndLoc) {
-  assert((ModifierLoc.isInvalid() || getLangOpts().OpenMP >= 60) &&
-         "Unexpected num_threads modifier in OpenMP < 60.");
-
-  if (ModifierLoc.isValid() && Modifier == OMPC_NUMTHREADS_unknown) {
-    std::string Values = getListOfPossibleValues(OMPC_num_threads, /*First=*/0,
-                                                 OMPC_NUMTHREADS_unknown);
-    Diag(ModifierLoc, diag::err_omp_unexpected_clause_value)
-        << Values << getOpenMPClauseNameForDiag(OMPC_num_threads);
-    return nullptr;
+    ArrayRef<Expr *> VarList,
+    OpenMPNumThreadsClauseModifier PrescriptivenessModifier,
+    SourceLocation PrescriptivenessModifierLoc,
+    OpenMPNumThreadsClauseModifier DimsModifier, Expr *DimsModifierExpr,
+    SourceLocation DimsModifierLoc, SourceLocation StartLoc,
+    SourceLocation LParenLoc, SourceLocation EndLoc) {
+  SmallVector<Expr *, 3> Vars(VarList.begin(), VarList.end());
+  for (Expr *&ValExpr : Vars) {
+    // OpenMP [2.5, Restrictions]
+    //  The num_threads expression must evaluate to a positive integer value.
+    if (!isNonNegativeIntegerValue(ValExpr, SemaRef, OMPC_num_threads,
+                                   /*StrictlyPositive=*/true))
+      return nullptr;
   }
 
-  Expr *ValExpr = NumThreads;
-  Stmt *HelperValStmt = nullptr;
+  if (DimsModifier == OMPC_NUMTHREADS_dims) {
+    ExprResult Res = ActOnOpenMPDimsModifier(OMPC_num_threads, DimsModifier,
+                                             DimsModifierExpr, DimsModifierLoc,
+                                             Vars, EndLoc);
+    if (Res.isInvalid())
+      return nullptr;
+    DimsModifierExpr = Res.get();
 
-  // OpenMP [2.5, Restrictions]
-  //  The num_threads expression must evaluate to a positive integer value.
-  if (!isNonNegativeIntegerValue(ValExpr, SemaRef, OMPC_num_threads,
-                                 /*StrictlyPositive=*/true))
+    if (validateMultidimClauseExprs(*this, OMPC_num_threads, StartLoc, Vars,
+                                    DimsModifierExpr))
+      return nullptr;
+  }
+  if (PrescriptivenessModifier == OMPC_NUMTHREADS_strict &&
+      getLangOpts().OpenMP < 60) {
+    Diag(PrescriptivenessModifierLoc, diag::err_omp_modifier_requires_version)
+        << getOpenMPSimpleClauseTypeName(llvm::omp::OMPC_num_threads,
+                                         PrescriptivenessModifier)
+        << getOpenMPClauseName(llvm::omp::OMPC_num_threads) << "6.0";
     return nullptr;
+  }
 
   OpenMPDirectiveKind DKind = DSAStack->getCurrentDirective();
   OpenMPDirectiveKind CaptureRegion = getOpenMPCaptureRegionForClause(
       DKind, OMPC_num_threads, getLangOpts().OpenMP);
-  if (CaptureRegion != OMPD_unknown &&
-      !SemaRef.CurContext->isDependentContext()) {
+  if (CaptureRegion == OMPD_unknown || 
SemaRef.CurContext->isDependentContext())
+    return OMPNumThreadsClause::Create(
+        getASTContext(), CaptureRegion, StartLoc, LParenLoc, EndLoc, Vars,
+        PrescriptivenessModifier, DimsModifier, PrescriptivenessModifierLoc,
+        DimsModifierLoc, DimsModifierExpr, /*PreInit=*/nullptr);
+
+  llvm::MapVector<const Expr *, DeclRefExpr *> Captures;
+  for (Expr *&ValExpr : Vars) {
     ValExpr = SemaRef.MakeFullExpr(ValExpr).get();
-    llvm::MapVector<const Expr *, DeclRefExpr *> Captures;
     ValExpr = tryBuildCapture(SemaRef, ValExpr, Captures).get();
-    HelperValStmt = buildPreInits(getASTContext(), Captures);
   }
+  Stmt *PreInit = buildPreInits(getASTContext(), Captures);
 
-  return new (getASTContext())
-      OMPNumThreadsClause(Modifier, ValExpr, HelperValStmt, CaptureRegion,
-                          StartLoc, LParenLoc, ModifierLoc, EndLoc);
+  return OMPNumThreadsClause::Create(
+      getASTContext(), CaptureRegion, StartLoc, LParenLoc, EndLoc, Vars,
+      PrescriptivenessModifier, DimsModifier, PrescriptivenessModifierLoc,
+      DimsModifierLoc, DimsModifierExpr, PreInit);
 }
 
 ExprResult SemaOpenMP::VerifyPositiveIntegerConstantInClause(
@@ -18398,13 +18417,6 @@ OMPClause 
*SemaOpenMP::ActOnOpenMPSingleExprWithArgClause(
         ArgumentLoc[Modifier2], EndLoc);
     break;
   }
-  case OMPC_num_threads:
-    assert(Argument.size() == 1 && ArgumentLoc.size() == 1 &&
-           "Modifier for num_threads clause and its location are expected.");
-    Res = ActOnOpenMPNumThreadsClause(
-        static_cast<OpenMPNumThreadsClauseModifier>(Argument.back()), Expr,
-        StartLoc, LParenLoc, ArgumentLoc.back(), EndLoc);
-    break;
   case OMPC_final:
   case OMPC_safelen:
   case OMPC_simdlen:
@@ -19387,10 +19399,23 @@ OMPClause 
*SemaOpenMP::ActOnOpenMPVarListClause(OpenMPClauseKind Kind,
         VarList, static_cast<OpenMPThreadLimitClauseModifier>(ExtraModifier),
         ExtraModifierExpr, ExtraModifierLoc, StartLoc, LParenLoc, EndLoc);
     break;
+  case OMPC_num_threads:
+    assert(0 <= Data.ExtraModifierArray[0] &&
+           Data.ExtraModifierArray[0] <= OMPC_NUMTHREADS_unknown &&
+           0 <= Data.ExtraModifierArray[1] &&
+           Data.ExtraModifierArray[1] <= OMPC_NUMTHREADS_unknown &&
+           "Unexpected num_threads modifier.");
+    Res = ActOnOpenMPNumThreadsClause(
+        VarList,
+        
static_cast<OpenMPNumThreadsClauseModifier>(Data.ExtraModifierArray[0]),
+        Data.ExtraModifierLocArray[0],
+        
static_cast<OpenMPNumThreadsClauseModifier>(Data.ExtraModifierArray[1]),
+        Data.ExtraModifierExprArray[1], Data.ExtraModifierLocArray[1], 
StartLoc,
+        LParenLoc, EndLoc);
+    break;
   case OMPC_if:
   case OMPC_depobj:
   case OMPC_final:
-  case OMPC_num_threads:
   case OMPC_safelen:
   case OMPC_simdlen:
   case OMPC_sizes:
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 92df2e622a37c..775d591787e5b 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -1730,14 +1730,17 @@ class TreeTransform {
   ///
   /// By default, performs semantic analysis to build the new OpenMP clause.
   /// Subclasses may override this routine to provide different behavior.
-  OMPClause *RebuildOMPNumThreadsClause(OpenMPNumThreadsClauseModifier 
Modifier,
-                                        Expr *NumThreads,
-                                        SourceLocation StartLoc,
-                                        SourceLocation LParenLoc,
-                                        SourceLocation ModifierLoc,
-                                        SourceLocation EndLoc) {
+  OMPClause *RebuildOMPNumThreadsClause(
+      ArrayRef<Expr *> VarList,
+      OpenMPNumThreadsClauseModifier PrescriptivenessModifier,
+      SourceLocation PrescriptivenessModifierLoc,
+      OpenMPNumThreadsClauseModifier DimsModifier, Expr *DimsModifierExpr,
+      SourceLocation DimsModifierLoc, SourceLocation StartLoc,
+      SourceLocation LParenLoc, SourceLocation EndLoc) {
     return getSema().OpenMP().ActOnOpenMPNumThreadsClause(
-        Modifier, NumThreads, StartLoc, LParenLoc, ModifierLoc, EndLoc);
+        VarList, PrescriptivenessModifier, PrescriptivenessModifierLoc,
+        DimsModifier, DimsModifierExpr, DimsModifierLoc, StartLoc, LParenLoc,
+        EndLoc);
   }
 
   /// Build a new OpenMP 'safelen' clause.
@@ -10586,12 +10589,26 @@ OMPClause 
*TreeTransform<Derived>::TransformOMPFinalClause(OMPFinalClause *C) {
 template <typename Derived>
 OMPClause *
 TreeTransform<Derived>::TransformOMPNumThreadsClause(OMPNumThreadsClause *C) {
-  ExprResult NumThreads = getDerived().TransformExpr(C->getNumThreads());
-  if (NumThreads.isInvalid())
-    return nullptr;
+  llvm::SmallVector<Expr *, 3> Vars;
+  Vars.reserve(C->varlist_size());
+  for (auto *VE : C->varlist()) {
+    ExprResult EVar = getDerived().TransformExpr(cast<Expr>(VE));
+    if (EVar.isInvalid())
+      return nullptr;
+    Vars.push_back(EVar.get());
+  }
+  Expr *DimsModifierExpr = C->getDimsModifierExpr();
+  if (DimsModifierExpr) {
+    ExprResult EVar = getDerived().TransformExpr(cast<Expr>(DimsModifierExpr));
+    if (EVar.isInvalid())
+      return nullptr;
+    DimsModifierExpr = EVar.get();
+  }
   return getDerived().RebuildOMPNumThreadsClause(
-      C->getModifier(), NumThreads.get(), C->getBeginLoc(), C->getLParenLoc(),
-      C->getModifierLoc(), C->getEndLoc());
+      Vars, C->getPrescriptivenessModifier(),
+      C->getPrescriptivenessModifierLoc(), C->getDimsModifier(),
+      DimsModifierExpr, C->getDimsModifierLoc(), C->getBeginLoc(),
+      C->getLParenLoc(), C->getEndLoc());
 }
 
 template <typename Derived>
diff --git a/clang/lib/Serialization/ASTReader.cpp 
b/clang/lib/Serialization/ASTReader.cpp
index 3189bf3431c1e..6a2118511abf6 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -11466,7 +11466,7 @@ OMPClause *OMPClauseReader::readClause() {
     C = new (Context) OMPFinalClause();
     break;
   case llvm::omp::OMPC_num_threads:
-    C = new (Context) OMPNumThreadsClause();
+    C = OMPNumThreadsClause::CreateEmpty(Context, Record.readInt());
     break;
   case llvm::omp::OMPC_safelen:
     C = new (Context) OMPSafelenClause();
@@ -11878,11 +11878,20 @@ void 
OMPClauseReader::VisitOMPFinalClause(OMPFinalClause *C) {
 }
 
 void OMPClauseReader::VisitOMPNumThreadsClause(OMPNumThreadsClause *C) {
+  C->setPrescriptivenessModifier(
+      Record.readEnum<OpenMPNumThreadsClauseModifier>());
+  C->setPrescriptivenessModifierLoc(Record.readSourceLocation());
+  C->setDimsModifier(Record.readEnum<OpenMPNumThreadsClauseModifier>());
+  C->setDimsModifierLoc(Record.readSourceLocation());
+  C->setDimsModifierExpr(Record.readSubExpr());
   VisitOMPClauseWithPreInit(C);
-  C->setModifier(Record.readEnum<OpenMPNumThreadsClauseModifier>());
-  C->setNumThreads(Record.readSubExpr());
-  C->setModifierLoc(Record.readSourceLocation());
   C->setLParenLoc(Record.readSourceLocation());
+  unsigned NumVars = C->varlist_size();
+  SmallVector<Expr *, 16> Vars;
+  Vars.reserve(NumVars);
+  for (unsigned I = 0; I != NumVars; ++I)
+    Vars.push_back(Record.readSubExpr());
+  C->setVarRefs(Vars);
 }
 
 void OMPClauseReader::VisitOMPSafelenClause(OMPSafelenClause *C) {
diff --git a/clang/lib/Serialization/ASTWriter.cpp 
b/clang/lib/Serialization/ASTWriter.cpp
index a305e92b6a602..9fcf4d269e9ea 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -8057,11 +8057,16 @@ void 
OMPClauseWriter::VisitOMPFinalClause(OMPFinalClause *C) {
 }
 
 void OMPClauseWriter::VisitOMPNumThreadsClause(OMPNumThreadsClause *C) {
+  Record.push_back(C->varlist_size());
+  Record.writeEnum(C->getPrescriptivenessModifier());
+  Record.AddSourceLocation(C->getPrescriptivenessModifierLoc());
+  Record.writeEnum(C->getDimsModifier());
+  Record.AddSourceLocation(C->getDimsModifierLoc());
+  Record.AddStmt(C->getDimsModifierExpr());
   VisitOMPClauseWithPreInit(C);
-  Record.writeEnum(C->getModifier());
-  Record.AddStmt(C->getNumThreads());
-  Record.AddSourceLocation(C->getModifierLoc());
   Record.AddSourceLocation(C->getLParenLoc());
+  for (auto *VE : C->varlist())
+    Record.AddStmt(VE);
 }
 
 void OMPClauseWriter::VisitOMPSafelenClause(OMPSafelenClause *C) {
diff --git a/clang/test/OpenMP/dims_modifier_ast_print.cpp 
b/clang/test/OpenMP/dims_modifier_ast_print.cpp
index d47e8353abcad..57801f305218f 100644
--- a/clang/test/OpenMP/dims_modifier_ast_print.cpp
+++ b/clang/test/OpenMP/dims_modifier_ast_print.cpp
@@ -21,6 +21,46 @@ void test_ast_print() {
   // CHECK: #pragma omp target teams num_teams(dims(1):N) 
thread_limit(dims(1):M)
   #pragma omp target teams num_teams(dims(1): N) thread_limit(dims(1): M)
   {}
+
+  // CHECK: #pragma omp parallel num_threads(dims(3): 1,2,3)
+  #pragma omp parallel num_threads(dims(3): 1, 2, 3)
+  {}
+
+  // CHECK: #pragma omp parallel num_threads(strict: 10)
+  #pragma omp parallel num_threads(strict: 10)
+  {}
+
+  // CHECK: #pragma omp parallel num_threads(strict,dims(2): N + 1,N)
+  #pragma omp parallel num_threads(strict, dims(2): N + 1, N)
+  {}
+
+  // CHECK: #pragma omp parallel num_threads(strict,dims(2): 10,20)
+  #pragma omp parallel num_threads(dims(2), strict: 10, 20)
+  {}
+
+  // CHECK: #pragma omp parallel num_threads(strict,dims(2): N,N + 1)
+  #pragma omp parallel num_threads(strict, dims(2): N, N + 1)
+  {}
+
+  // CHECK: #pragma omp target parallel thread_limit(dims(2):10,20) 
num_threads(dims(2): N,N + 1)
+  #pragma omp target parallel thread_limit(dims(2): 10, 20) 
num_threads(dims(2): N, N + 1)
+  {}
+
+  // CHECK: #pragma omp target teams num_teams(dims(3):1,2,3) 
thread_limit(dims(2):10,20)
+  // CHECK-NEXT: #pragma omp parallel num_threads(dims(2): N,N + 1)
+  #pragma omp target teams num_teams(dims(3): 1, 2, 3) thread_limit(dims(2): 
10, 20)
+  #pragma omp parallel num_threads(dims(2): N, N + 1)
+  {}
+
+  // CHECK: #pragma omp target parallel thread_limit(dims(1):M) 
num_threads(strict,dims(2): 10,20)
+  #pragma omp target parallel thread_limit(dims(1): M) num_threads(dims(2), 
strict: 10, 20)
+  {}
+
+  // CHECK: #pragma omp target teams num_teams(dims(1):N) 
thread_limit(dims(1):M)
+  // CHECK-NEXT: #pragma omp parallel num_threads(strict,dims(2): 10,20)
+  #pragma omp target teams num_teams(dims(1): N) thread_limit(dims(1): M)
+  #pragma omp parallel num_threads(dims(2), strict: 10, 20)
+  {}
 }
 
 template <int D>
@@ -33,6 +73,28 @@ void template_test() {
   // CHECK: #pragma omp target teams num_teams(dims(3):arr[0],arr[1],arr[2]) 
thread_limit(dims(2):3,arr[0])
   #pragma omp target teams num_teams(dims(D): arr[0], arr[1], arr[2]) 
thread_limit(dims(2): D, arr[0])
   {}
+
+  // CHECK: #pragma omp parallel num_threads(dims(3): arr[0],arr[1],arr[2])
+  #pragma omp parallel num_threads(dims(3): arr[0], arr[1], arr[2])
+  {}
+
+  // CHECK: #pragma omp parallel num_threads(strict,dims(2): 3,arr[0])
+  #pragma omp parallel num_threads(strict, dims(2): D, arr[0])
+  {}
+
+  // CHECK: #pragma omp parallel num_threads(strict,dims(2): 3,arr[0])
+  #pragma omp parallel num_threads(dims(2), strict: D, arr[0])
+  {}
+
+  // CHECK: #pragma omp target parallel thread_limit(dims(1):3) 
num_threads(strict,dims(2): 3,arr[0])
+  #pragma omp target parallel thread_limit(dims(1): D) num_threads(dims(2), 
strict: D, arr[0])
+  {}
+
+  // CHECK: #pragma omp target teams num_teams(dims(3):arr[0],arr[1],arr[2]) 
thread_limit(dims(1):3)
+  // CHECK-NEXT: #pragma omp parallel num_threads(strict,dims(2): 3,arr[0])
+  #pragma omp target teams num_teams(dims(3): arr[0], arr[1], arr[2]) 
thread_limit(dims(1): D)
+  #pragma omp parallel num_threads(dims(2), strict: D, arr[0])
+  {}
 }
 
 void call_templates() {
diff --git a/clang/test/OpenMP/dims_modifier_messages.cpp 
b/clang/test/OpenMP/dims_modifier_messages.cpp
index ce0dd7eb79c52..e7e74e900cf1d 100644
--- a/clang/test/OpenMP/dims_modifier_messages.cpp
+++ b/clang/test/OpenMP/dims_modifier_messages.cpp
@@ -8,17 +8,25 @@ void foo() {
 }
 
 #ifndef VERSION52
-void bar(int N) { // expected-note {{declared here}}
+void bar(int N) { // expected-note 2 {{declared here}}
   // 1. Invalid syntax of the dims modifier.
 
 #pragma omp target teams num_teams(dims 2: 4) // expected-error {{use of 
undeclared identifier 'dims'}}
   foo();
 
+#pragma omp parallel num_threads(dims 2: 4) // expected-error {{use of 
undeclared identifier 'dims'}}
+  foo();
+
 #pragma omp target thread_limit(dim(2) 4, 5)
   // expected-error@-1 {{use of undeclared identifier 'dim'}}
   // expected-error@-2 {{expected ',' or ')' in 'thread_limit' clause}}
   foo();
 
+#pragma omp parallel num_threads(dim(2) 4, 5)
+  // expected-error@-1 {{use of undeclared identifier 'dim'}}
+  // expected-error@-2 {{expected ',' or ')' in 'num_threads' clause}}
+  foo();
+
 #pragma omp target thread_limit(dims((2): 4, 5)
   // expected-error@-1 {{expected ')'}}
   // expected-error@-2 {{expected ')'}}
@@ -27,17 +35,36 @@ void bar(int N) { // expected-note {{declared here}}
   // expected-error@-5 {{missing ':' after thread_limit modifier}}
   foo();
 
+#pragma omp parallel num_threads(dims((2): 4, 5)
+  // expected-error@-1 {{expected ')'}}
+  // expected-error@-2 {{expected ')'}}
+  // expected-note@-3 {{to match this '('}}
+  // expected-note@-4 {{to match this '('}}
+  // expected-error@-5 {{missing ':' after num_threads modifier}}
+  foo();
+
 #pragma omp target thread_limit(dims(2)): 4, 5)
   // expected-error@-1 {{missing ':' after thread_limit modifier}}
   // expected-warning@-2 {{extra tokens at the end of '#pragma omp target' are 
ignored}}
   foo();
 
+#pragma omp parallel num_threads(dims(2)): 4, 5)
+  // expected-error@-1 {{missing ':' after num_threads modifier}}
+  // expected-warning@-2 {{extra tokens at the end of '#pragma omp parallel' 
are ignored}}
+  foo();
+
 #pragma omp target thread_limit(dims(2) 4, 5) // expected-error {{missing ':' 
after thread_limit modifier}}
   foo();
 
+#pragma omp parallel num_threads(dims(2) 4, 5) // expected-error {{missing ':' 
after num_threads modifier}}
+  foo();
+
 #pragma omp target teams distribute num_teams(dims(): 4) // expected-error 
{{expected expression}}
   for (int i = 0; i < 10; ++i) {}
 
+#pragma omp parallel num_threads(dims(): 4) // expected-error {{expected 
expression}}
+  foo();
+
   // 3. Incompatible modifiers.
 
 #pragma omp target teams num_teams(dims(1),10:20) // expected-error 
{{'lower_bound' modifier cannot be specified with 'dims' modifier in 
'num_teams' clause}}
@@ -51,6 +78,9 @@ void bar(int N) { // expected-note {{declared here}}
 #pragma omp target thread_limit(dims(1): 4, 5) // expected-error {{unexpected 
number of expressions in 'thread_limit' clause}}
   foo();
 
+#pragma omp parallel num_threads(dims(2): 4) // expected-error {{unexpected 
number of expressions in 'num_threads' clause}}
+  foo();
+
 #pragma omp target teams distribute num_teams(dims(3): 4, 5) // expected-error 
{{unexpected number of expressions in 'num_teams' clause}}
   for (int i = 0; i < 10; ++i) {}
 
@@ -62,6 +92,9 @@ void bar(int N) { // expected-note {{declared here}}
 #pragma omp target thread_limit(dims(2): 1, 2, 3, 4) // expected-error 
{{unexpected number of expressions in 'thread_limit' clause}}
   foo();
 
+#pragma omp parallel num_threads(dims(4): 1, 2, 3, 4) // expected-error 
{{maximum three expressions are supported in 'num_threads' clause}}
+  foo();
+
 #pragma omp target teams distribute thread_limit(dims(4): 1, 2, 3, 4) // 
expected-error {{maximum three expressions are supported in 'thread_limit' 
clause}}
   for (int i = 0; i < 10; ++i) {}
 
@@ -80,14 +113,25 @@ void bar(int N) { // expected-note {{declared here}}
   // expected-note@-2 {{function parameter 'N' with unknown value cannot be 
used in a constant expression}}
   foo();
 
+#pragma omp parallel num_threads(dims(N): 1, 2)
+  // expected-error@-1 {{expression is not an integral constant expression}}
+  // expected-note@-2 {{function parameter 'N' with unknown value cannot be 
used in a constant expression}}
+  foo();
+
 #pragma omp target thread_limit(dims(2.5): 1, 2) // expected-error {{integral 
constant expression must have integral or unscoped enumeration type, not 
'double'}}
   foo();
 
+#pragma omp parallel num_threads(dims(2.5): 1, 2) // expected-error {{integral 
constant expression must have integral or unscoped enumeration type, not 
'double'}}
+  foo();
+
 #pragma omp target teams distribute num_teams(dims(0): 4) // expected-error 
{{argument to 'num_teams' clause must be a strictly positive integer value}}
   for (int i = 0; i < 10; ++i) {}
 
 #pragma omp target teams thread_limit(dims(-1): 4) // expected-error 
{{argument to 'thread_limit' clause must be a strictly positive integer value}}
   foo();
+
+#pragma omp parallel num_threads(dims(0): 4) // expected-error {{argument to 
'num_threads' clause must be a strictly positive integer value}}
+  foo();
 }
 
 template <int D>
@@ -104,6 +148,11 @@ void template_test() {
   // expected-error@-2 {{argument to 'thread_limit' clause must be a strictly 
positive integer value}}
   foo();
 
+#pragma omp parallel num_threads(dims(D): 4)
+  // expected-error@-1 {{unexpected number of expressions in 'num_threads' 
clause}}
+  // expected-error@-2 {{argument to 'num_threads' clause must be a strictly 
positive integer value}}
+  foo();
+
 #pragma omp target teams distribute num_teams(dims(D): 4, 5)
   // expected-error@-1 {{unexpected number of expressions in 'num_teams' 
clause}}
   // expected-error@-2 {{argument to 'num_teams' clause must be a strictly 
positive integer value}}
@@ -125,5 +174,8 @@ void version() {
 
 #pragma omp target thread_limit(dims(1): 4) // expected-error {{'dims' 
modifier in 'thread_limit' clause requires OpenMP 6.1 or later}}
   foo();
+
+#pragma omp target parallel num_threads(dims(1): 4) // expected-error {{'dims' 
modifier in 'num_threads' clause requires OpenMP 6.1 or later}}
+  foo();
 }
 #endif
diff --git a/clang/test/OpenMP/distribute_parallel_for_num_threads_messages.cpp 
b/clang/test/OpenMP/distribute_parallel_for_num_threads_messages.cpp
index ab301cc517207..296d0865d14e3 100644
--- a/clang/test/OpenMP/distribute_parallel_for_num_threads_messages.cpp
+++ b/clang/test/OpenMP/distribute_parallel_for_num_threads_messages.cpp
@@ -48,7 +48,7 @@ T tmain(T argc, S **argv) {
   for (i = 0; i < argc; ++i) foo();
 #pragma omp target
 #pragma omp teams
-#pragma omp distribute parallel for num_threads (argv[1]=2) // expected-error 
{{expected ')'}} expected-note {{to match this '('}} expected-error 2 
{{expression must have integral or unscoped enumeration type, not 'char *'}}
+#pragma omp distribute parallel for num_threads (argv[1]=2) // expected-error 
2 {{incompatible integer to pointer conversion assigning to 'char *' from 
'int'}}
   for (i = 0; i < argc; ++i) foo();
 #pragma omp target
 #pragma omp teams
@@ -98,7 +98,7 @@ int main(int argc, char **argv) {
   for (i = 0; i < argc; ++i) foo();
 #pragma omp target
 #pragma omp teams
-#pragma omp distribute parallel for num_threads (argv[1]=2) // expected-error 
{{expected ')'}} expected-note {{to match this '('}} expected-error 
{{expression must have integral or unscoped enumeration type, not 'char *'}}
+#pragma omp distribute parallel for num_threads (argv[1]=2) // expected-error 
{{incompatible integer to pointer conversion assigning to 'char *' from 'int'}}
   for (i = 0; i < argc; ++i) foo();
 #pragma omp target
 #pragma omp teams
diff --git 
a/clang/test/OpenMP/distribute_parallel_for_simd_num_threads_messages.cpp 
b/clang/test/OpenMP/distribute_parallel_for_simd_num_threads_messages.cpp
index 9f9de197aca0d..d405246cd5a56 100644
--- a/clang/test/OpenMP/distribute_parallel_for_simd_num_threads_messages.cpp
+++ b/clang/test/OpenMP/distribute_parallel_for_simd_num_threads_messages.cpp
@@ -48,7 +48,7 @@ T tmain(T argc, S **argv) {
   for (i = 0; i < argc; ++i) foo();
 #pragma omp target
 #pragma omp teams
-#pragma omp distribute parallel for simd num_threads (argv[1]=2) // 
expected-error {{expected ')'}} expected-note {{to match this '('}} 
expected-error 2 {{expression must have integral or unscoped enumeration type, 
not 'char *'}}
+#pragma omp distribute parallel for simd num_threads (argv[1]=2) // 
expected-error 2 {{incompatible integer to pointer conversion assigning to 
'char *' from 'int'}}
   for (i = 0; i < argc; ++i) foo();
 #pragma omp target
 #pragma omp teams
@@ -98,7 +98,7 @@ int main(int argc, char **argv) {
   for (i = 0; i < argc; ++i) foo();
 #pragma omp target
 #pragma omp teams
-#pragma omp distribute parallel for simd num_threads (argv[1]=2) // 
expected-error {{expected ')'}} expected-note {{to match this '('}} 
expected-error {{expression must have integral or unscoped enumeration type, 
not 'char *'}}
+#pragma omp distribute parallel for simd num_threads (argv[1]=2) // 
expected-error {{incompatible integer to pointer conversion assigning to 'char 
*' from 'int'}}
   for (i = 0; i < argc; ++i) foo();
 #pragma omp target
 #pragma omp teams
diff --git a/clang/test/OpenMP/parallel_for_num_threads_messages.cpp 
b/clang/test/OpenMP/parallel_for_num_threads_messages.cpp
index e689df8fc7d85..4979e7df7e1a0 100644
--- a/clang/test/OpenMP/parallel_for_num_threads_messages.cpp
+++ b/clang/test/OpenMP/parallel_for_num_threads_messages.cpp
@@ -30,7 +30,7 @@ T tmain(T argc, S **argv) {
   for (i = 0; i < argc; ++i) foo();
   #pragma omp parallel for num_threads (S) // expected-error {{'S' does not 
refer to a value}}
   for (i = 0; i < argc; ++i) foo();
-  #pragma omp parallel for num_threads (argv[1]=2) // expected-error 
{{expected ')'}} expected-note {{to match this '('}} expected-error 2 
{{expression must have integral or unscoped enumeration type, not 'char *'}}
+  #pragma omp parallel for num_threads (argv[1]=2) // expected-error 2 
{{incompatible integer to pointer conversion assigning to 'char *' from 'int'}}
   for (i = 0; i < argc; ++i) foo();
   #pragma omp parallel for num_threads (argc+z)
   for (i = 0; i < argc; ++i) foo();
@@ -58,7 +58,7 @@ int main(int argc, char **argv) {
   for (i = 0; i < argc; ++i) foo();
   #pragma omp parallel for num_threads (S1) // expected-error {{'S1' does not 
refer to a value}}
   for (i = 0; i < argc; ++i) foo();
-  #pragma omp parallel for num_threads (argv[1]=2) // expected-error 
{{expected ')'}} expected-note {{to match this '('}} expected-error 
{{expression must have integral or unscoped enumeration type, not 'char *'}}
+  #pragma omp parallel for num_threads (argv[1]=2) // expected-error 
{{incompatible integer to pointer conversion assigning to 'char *' from 'int'}}
   for (i = 0; i < argc; ++i) foo();
   #pragma omp parallel for num_threads (num_threads(tmain<int, char, -1>(argc, 
argv) // expected-error 2 {{expected ')'}} expected-note 2 {{to match this 
'('}} expected-note {{in instantiation of function template specialization 
'tmain<int, char, -1>' requested here}}
   for (i = 0; i < argc; ++i) foo();
diff --git a/clang/test/OpenMP/parallel_for_simd_num_threads_messages.cpp 
b/clang/test/OpenMP/parallel_for_simd_num_threads_messages.cpp
index f79ec4f68f4bb..f7457697bc227 100644
--- a/clang/test/OpenMP/parallel_for_simd_num_threads_messages.cpp
+++ b/clang/test/OpenMP/parallel_for_simd_num_threads_messages.cpp
@@ -30,7 +30,7 @@ T tmain(T argc, S **argv) {
   for (i = 0; i < argc; ++i) foo();
   #pragma omp parallel for simd num_threads (S) // expected-error {{'S' does 
not refer to a value}}
   for (i = 0; i < argc; ++i) foo();
-  #pragma omp parallel for simd num_threads (argv[1]=2) // expected-error 
{{expected ')'}} expected-note {{to match this '('}} expected-error 2 
{{expression must have integral or unscoped enumeration type, not 'char *'}}
+  #pragma omp parallel for simd num_threads (argv[1]=2) // expected-error 2 
{{incompatible integer to pointer conversion assigning to 'char *' from 'int'}}
   for (i = 0; i < argc; ++i) foo();
   #pragma omp parallel for simd num_threads (argc + z)
   for (i = 0; i < argc; ++i) foo();
@@ -58,7 +58,7 @@ int main(int argc, char **argv) {
   for (i = 0; i < argc; ++i) foo();
   #pragma omp parallel for simd num_threads (S1) // expected-error {{'S1' does 
not refer to a value}}
   for (i = 0; i < argc; ++i) foo();
-  #pragma omp parallel for simd num_threads (argv[1]=2) // expected-error 
{{expected ')'}} expected-note {{to match this '('}} expected-error 
{{expression must have integral or unscoped enumeration type, not 'char *'}}
+  #pragma omp parallel for simd num_threads (argv[1]=2) // expected-error 
{{incompatible integer to pointer conversion assigning to 'char *' from 'int'}}
   for (i = 0; i < argc; ++i) foo();
   #pragma omp parallel for simd num_threads (num_threads(tmain<int, char, 
-1>(argc, argv) // expected-error 2 {{expected ')'}} expected-note 2 {{to match 
this '('}} expected-note {{in instantiation of function template specialization 
'tmain<int, char, -1>' requested here}}
   for (i = 0; i < argc; ++i) foo();
diff --git a/clang/test/OpenMP/parallel_masked_num_threads_messages.cpp 
b/clang/test/OpenMP/parallel_masked_num_threads_messages.cpp
index 928c07c5610e1..46dc4035cc9d6 100644
--- a/clang/test/OpenMP/parallel_masked_num_threads_messages.cpp
+++ b/clang/test/OpenMP/parallel_masked_num_threads_messages.cpp
@@ -30,7 +30,7 @@ T tmain(T argc, S **argv) {
   {foo();}
   #pragma omp parallel masked num_threads (S) // expected-error {{'S' does not 
refer to a value}}
   {foo();}
-  #pragma omp parallel masked num_threads (argv[1]=2) // expected-error 
{{expected ')'}} expected-note {{to match this '('}} expected-error 2 
{{expression must have integral or unscoped enumeration type, not 'char *'}}
+  #pragma omp parallel masked num_threads (argv[1]=2) // expected-error 2 
{{incompatible integer to pointer conversion assigning to 'char *' from 'int'}}
   {foo();}
   #pragma omp parallel masked num_threads (argc + z)
   {foo();}
@@ -58,7 +58,7 @@ int main(int argc, char **argv) {
   {foo();}
   #pragma omp parallel masked num_threads (S1) // expected-error {{'S1' does 
not refer to a value}}
   {foo();}
-  #pragma omp parallel masked num_threads (argv[1]=2) // expected-error 
{{expected ')'}} expected-note {{to match this '('}} expected-error 
{{expression must have integral or unscoped enumeration type, not 'char *'}}
+  #pragma omp parallel masked num_threads (argv[1]=2) // expected-error 
{{incompatible integer to pointer conversion assigning to 'char *' from 'int'}}
   {foo();}
   #pragma omp parallel masked num_threads (num_threads(tmain<int, char, 
-1>(argc, argv) // expected-error 2 {{expected ')'}} expected-note 2 {{to match 
this '('}} expected-note {{in instantiation of function template specialization 
'tmain<int, char, -1>' requested here}}
   {foo();}
diff --git a/clang/test/OpenMP/parallel_master_num_threads_messages.cpp 
b/clang/test/OpenMP/parallel_master_num_threads_messages.cpp
index 601164f1e5328..d951abccb12c0 100644
--- a/clang/test/OpenMP/parallel_master_num_threads_messages.cpp
+++ b/clang/test/OpenMP/parallel_master_num_threads_messages.cpp
@@ -30,7 +30,7 @@ T tmain(T argc, S **argv) {
   {foo();}
   #pragma omp parallel master num_threads (S) // expected-error {{'S' does not 
refer to a value}}
   {foo();}
-  #pragma omp parallel master num_threads (argv[1]=2) // expected-error 
{{expected ')'}} expected-note {{to match this '('}} expected-error 2 
{{expression must have integral or unscoped enumeration type, not 'char *'}}
+  #pragma omp parallel master num_threads (argv[1]=2) // expected-error 2 
{{incompatible integer to pointer conversion assigning to 'char *' from 'int'}}
   {foo();}
   #pragma omp parallel master num_threads (argc + z)
   {foo();}
@@ -58,7 +58,7 @@ int main(int argc, char **argv) {
   {foo();}
   #pragma omp parallel master num_threads (S1) // expected-error {{'S1' does 
not refer to a value}}
   {foo();}
-  #pragma omp parallel master num_threads (argv[1]=2) // expected-error 
{{expected ')'}} expected-note {{to match this '('}} expected-error 
{{expression must have integral or unscoped enumeration type, not 'char *'}}
+  #pragma omp parallel master num_threads (argv[1]=2) // expected-error 
{{incompatible integer to pointer conversion assigning to 'char *' from 'int'}}
   {foo();}
   #pragma omp parallel master num_threads (num_threads(tmain<int, char, 
-1>(argc, argv) // expected-error 2 {{expected ')'}} expected-note 2 {{to match 
this '('}} expected-note {{in instantiation of function template specialization 
'tmain<int, char, -1>' requested here}}
   {foo();}
diff --git a/clang/test/OpenMP/parallel_num_threads_messages.cpp 
b/clang/test/OpenMP/parallel_num_threads_messages.cpp
index 02c1cb6e69d99..fbffa06c29d6d 100644
--- a/clang/test/OpenMP/parallel_num_threads_messages.cpp
+++ b/clang/test/OpenMP/parallel_num_threads_messages.cpp
@@ -26,7 +26,7 @@ T tmain(T argc, S **argv) {
   #pragma omp parallel num_threads ((argc > 0) ? argv[1] : argv[2]) // 
expected-error 2 {{expression must have integral or unscoped enumeration type, 
not 'char *'}}
   #pragma omp parallel num_threads (foobool(argc)), num_threads (true), 
num_threads (-5) // expected-error 2 {{directive '#pragma omp parallel' cannot 
contain more than one 'num_threads' clause}} expected-error {{argument to 
'num_threads' clause must be a strictly positive integer value}}
   #pragma omp parallel num_threads (S) // expected-error {{'S' does not refer 
to a value}}
-  #pragma omp parallel num_threads (argv[1]=2) // expected-error {{expected 
')'}} expected-note {{to match this '('}} expected-error 2 {{expression must 
have integral or unscoped enumeration type, not 'char *'}}
+  #pragma omp parallel num_threads (argv[1]=2) // expected-error 2 
{{incompatible integer to pointer conversion assigning to 'char *' from 'int'}}
   #pragma omp parallel num_threads (argc+z)
   #pragma omp parallel num_threads (N) // expected-error {{argument to 
'num_threads' clause must be a strictly positive integer value}}
   #pragma omp parallel redef_num_threads (argc, argc)
@@ -42,23 +42,23 @@ T tmain(T argc, S **argv) {
   #pragma omp parallel num_threads(strict: // omp60-error {{expected 
expression}} omp60-error {{expected ')'}} omp60-note {{to match this '('}}
 
   // Invalid: unknown/missing modifier
-  #pragma omp parallel num_threads(foo: 4) // omp60-error {{expected 'strict' 
in OpenMP clause 'num_threads'}}
-  #pragma omp parallel num_threads(: 4) // omp60-error {{expected expression}} 
omp60-error {{expected ')'}} omp60-note {{to match this '('}}
-  #pragma omp parallel num_threads(:)// omp60-error {{expected expression}} 
omp60-error {{expected ')'}} omp60-note {{to match this '('}}
+  #pragma omp parallel num_threads(foo: 4) // omp60-error {{expected ',' or 
')' in 'num_threads' clause}} omp60-error {{expected ')'}} omp60-note {{to 
match this '('}} omp60-error 3 {{expression must have integral or unscoped 
enumeration type, not 'void ()'}}
+  #pragma omp parallel num_threads(: 4) // omp60-error {{expected expression}}
+  #pragma omp parallel num_threads(:)// omp60-error {{expected expression}}
 
   // Invalid: missing colon after modifier
-  #pragma omp parallel num_threads(strict 4) // omp60-error {{missing ':' 
after strict modifier}}
+  #pragma omp parallel num_threads(strict 4) // omp60-error {{missing ':' 
after num_threads modifier}}
 
   // Invalid: negative, zero, or non-integral
   #pragma omp parallel num_threads(strict: -1) // omp60-error {{argument to 
'num_threads' clause must be a strictly positive integer value}}
   #pragma omp parallel num_threads(strict: 0) // omp60-error {{argument to 
'num_threads' clause must be a strictly positive integer value}}
   #pragma omp parallel num_threads(strict: (argc > 0) ? argv[1] : argv[2]) // 
omp60-error 2 {{expression must have integral or unscoped enumeration type, not 
'char *'}}
   #pragma omp parallel num_threads(strict: S) // omp60-error {{'S' does not 
refer to a value}}
-  #pragma omp parallel num_threads(strict: argv[1]=2) // omp60-error 
{{expected ')'}} omp60-note {{to match this '('}} omp60-error 2 {{expression 
must have integral or unscoped enumeration type, not 'char *'}}
+  #pragma omp parallel num_threads(strict: argv[1]=2) // omp60-error 2 
{{incompatible integer to pointer conversion assigning to 'char *' from 'int'}}
   #pragma omp parallel num_threads(strict: N) // omp60-error {{argument to 
'num_threads' clause must be a strictly positive integer value}}
 
   // Invalid: multiple strict modifiers or mixed with non-strict
-  #pragma omp parallel num_threads(strict: 4, strict: 5) // omp60-error 
{{expected ')'}} expected-note {{to match this '('}}
+  #pragma omp parallel num_threads(strict: 4, strict: 5) // omp60-error {{use 
of undeclared identifier 'strict'}}
   #pragma omp parallel num_threads(strict: 4), num_threads(5) // omp60-error 
{{directive '#pragma omp parallel' cannot contain more than one 'num_threads' 
clause}}
   #pragma omp parallel num_threads(4), num_threads(strict: 5)  // omp60-error 
{{directive '#pragma omp parallel' cannot contain more than one 'num_threads' 
clause}}
 #endif // OMP60
@@ -78,7 +78,7 @@ int z;
   #pragma omp parallel num_threads (argc > 0 ? argv[1] : argv[2]) // 
expected-error {{integral }}
   #pragma omp parallel num_threads (foobool(argc)), num_threads (true), 
num_threads (-5) // expected-error 2 {{directive '#pragma omp parallel' cannot 
contain more than one 'num_threads' clause}} expected-error {{argument to 
'num_threads' clause must be a strictly positive integer value}}
   #pragma omp parallel num_threads (S1) // expected-error {{'S1' does not 
refer to a value}}
-  #pragma omp parallel num_threads (argv[1]=2) // expected-error {{expected 
')'}} expected-note {{to match this '('}} expected-error {{expression must have 
integral or unscoped enumeration type, not 'char *'}}
+  #pragma omp parallel num_threads (argv[1]=2) // expected-error 
{{incompatible integer to pointer conversion assigning to 'char *' from 'int'}}
   #pragma omp parallel num_threads (num_threads(tmain<int, char, -1>(argc, 
argv) // expected-error 2 {{expected ')'}} expected-note 2 {{to match this 
'('}} expected-note {{in instantiation of function template specialization 
'tmain<int, char, -1>' requested here}}
   #pragma omp parallel redef_num_threads (argc, argc)
 
@@ -93,22 +93,22 @@ int z;
   #pragma omp parallel num_threads(strict: // omp60-error {{expected 
expression}} omp60-error {{expected ')'}} omp60-note {{to match this '('}}
 
   // Invalid: unknown/missing modifier
-  #pragma omp parallel num_threads(foo: 4) // omp60-error {{expected 'strict' 
in OpenMP clause 'num_threads'}}
-  #pragma omp parallel num_threads(: 4) // omp60-error {{expected expression}} 
omp60-error {{expected ')'}} omp60-note {{to match this '('}}
-  #pragma omp parallel num_threads(:) // omp60-error {{expected expression}} 
omp60-error {{expected ')'}} omp60-note {{to match this '('}}
+  #pragma omp parallel num_threads(foo: 4) // omp60-error {{expected ',' or 
')' in 'num_threads' clause}} omp60-error {{expected ')'}} omp60-note {{to 
match this '('}} omp60-error {{expression must have integral or unscoped 
enumeration type, not 'void ()'}}
+  #pragma omp parallel num_threads(: 4) // omp60-error {{expected expression}}
+  #pragma omp parallel num_threads(:) // omp60-error {{expected expression}}
 
   // Invalid: missing colon after modifier
-  #pragma omp parallel num_threads(strict 4) // omp60-error {{missing ':' 
after strict modifier}}
+  #pragma omp parallel num_threads(strict 4) // omp60-error {{missing ':' 
after num_threads modifier}}
 
   // Invalid: negative, zero, or non-integral
   #pragma omp parallel num_threads(strict: -1) // omp60-error {{argument to 
'num_threads' clause must be a strictly positive integer value}}
   #pragma omp parallel num_threads(strict: 0) // omp60-error {{argument to 
'num_threads' clause must be a strictly positive integer value}}
   #pragma omp parallel num_threads(strict: (argc > 0) ? argv[1] : argv[2]) // 
omp60-error {{expression must have integral or unscoped enumeration type, not 
'char *'}}
   #pragma omp parallel num_threads(strict: S1) // omp60-error {{'S1' does not 
refer to a value}}
-  #pragma omp parallel num_threads(strict: argv[1]=2) // omp60-error 
{{expected ')'}} omp60-note {{to match this '('}} omp60-error {{expression must 
have integral or unscoped enumeration type, not 'char *'}}
+  #pragma omp parallel num_threads(strict: argv[1]=2) // omp60-error 
{{incompatible integer to pointer conversion assigning to 'char *' from 'int'}}
 
   // Invalid: multiple strict modifiers or mixed with non-strict
-  #pragma omp parallel num_threads(strict: 4, strict: 5) // omp60-error 
{{expected ')'}} expected-note {{to match this '('}}
+  #pragma omp parallel num_threads(strict: 4, strict: 5) // omp60-error {{use 
of undeclared identifier 'strict'}}
   #pragma omp parallel num_threads(strict: 4), num_threads(5) // omp60-error 
{{directive '#pragma omp parallel' cannot contain more than one 'num_threads' 
clause}}
   #pragma omp parallel num_threads(4), num_threads(strict: 5) // omp60-error 
{{directive '#pragma omp parallel' cannot contain more than one 'num_threads' 
clause}}
 #endif // OMP60
diff --git a/clang/test/OpenMP/parallel_sections_num_threads_messages.cpp 
b/clang/test/OpenMP/parallel_sections_num_threads_messages.cpp
index 31434e4757ab2..f396eb41e983e 100644
--- a/clang/test/OpenMP/parallel_sections_num_threads_messages.cpp
+++ b/clang/test/OpenMP/parallel_sections_num_threads_messages.cpp
@@ -30,7 +30,7 @@ T tmain(T argc, S **argv) {
   {foo();}
   #pragma omp parallel sections num_threads (S) // expected-error {{'S' does 
not refer to a value}}
   {foo();}
-  #pragma omp parallel sections num_threads (argv[1]=2) // expected-error 
{{expected ')'}} expected-note {{to match this '('}} expected-error 2 
{{expression must have integral or unscoped enumeration type, not 'char *'}}
+  #pragma omp parallel sections num_threads (argv[1]=2) // expected-error 2 
{{incompatible integer to pointer conversion assigning to 'char *' from 'int'}}
   {foo();}
   #pragma omp parallel sections num_threads (argc + z)
   {foo();}
@@ -58,7 +58,7 @@ int main(int argc, char **argv) {
   {foo();}
   #pragma omp parallel sections num_threads (S1) // expected-error {{'S1' does 
not refer to a value}}
   {foo();}
-  #pragma omp parallel sections num_threads (argv[1]=2) // expected-error 
{{expected ')'}} expected-note {{to match this '('}} expected-error 
{{expression must have integral or unscoped enumeration type, not 'char *'}}
+  #pragma omp parallel sections num_threads (argv[1]=2) // expected-error 
{{incompatible integer to pointer conversion assigning to 'char *' from 'int'}}
   {foo();}
   #pragma omp parallel sections num_threads (num_threads(tmain<int, char, 
-1>(argc, argv) // expected-error 2 {{expected ')'}} expected-note 2 {{to match 
this '('}} expected-note {{in instantiation of function template specialization 
'tmain<int, char, -1>' requested here}}
   {foo();}
diff --git a/clang/test/OpenMP/target_parallel_for_num_threads_messages.cpp 
b/clang/test/OpenMP/target_parallel_for_num_threads_messages.cpp
index 11f0c90780471..fddf0f8b05ef2 100644
--- a/clang/test/OpenMP/target_parallel_for_num_threads_messages.cpp
+++ b/clang/test/OpenMP/target_parallel_for_num_threads_messages.cpp
@@ -30,7 +30,7 @@ T tmain(T argc, S **argv) {
   for (i = 0; i < argc; ++i) foo();
   #pragma omp target parallel for num_threads (S) // expected-error {{'S' does 
not refer to a value}}
   for (i = 0; i < argc; ++i) foo();
-  #pragma omp target parallel for num_threads (argv[1]=2) // expected-error 
{{expected ')'}} expected-note {{to match this '('}} expected-error 2 
{{expression must have integral or unscoped enumeration type, not 'char *'}}
+  #pragma omp target parallel for num_threads (argv[1]=2) // expected-error 2 
{{incompatible integer to pointer conversion assigning to 'char *' from 'int'}}
   for (i = 0; i < argc; ++i) foo();
   #pragma omp target parallel for num_threads (argc)
   for (i = 0; i < argc; ++i) foo();
@@ -58,7 +58,7 @@ int main(int argc, char **argv) {
   for (i = 0; i < argc; ++i) foo();
   #pragma omp target parallel for num_threads (S1) // expected-error {{'S1' 
does not refer to a value}}
   for (i = 0; i < argc; ++i) foo();
-  #pragma omp target parallel for num_threads (argv[1]=2) // expected-error 
{{expected ')'}} expected-note {{to match this '('}} expected-error 
{{expression must have integral or unscoped enumeration type, not 'char *'}}
+  #pragma omp target parallel for num_threads (argv[1]=2) // expected-error 
{{incompatible integer to pointer conversion assigning to 'char *' from 'int'}}
   for (i = 0; i < argc; ++i) foo();
   #pragma omp target parallel for num_threads (num_threads(tmain<int, char, 
-1>(argc, argv) // expected-error 2 {{expected ')'}} expected-note 2 {{to match 
this '('}} expected-note {{in instantiation of function template specialization 
'tmain<int, char, -1>' requested here}}
   for (i = 0; i < argc; ++i) foo();
diff --git 
a/clang/test/OpenMP/target_parallel_for_simd_num_threads_messages.cpp 
b/clang/test/OpenMP/target_parallel_for_simd_num_threads_messages.cpp
index f7979057ae2b4..39b75ce91de8f 100644
--- a/clang/test/OpenMP/target_parallel_for_simd_num_threads_messages.cpp
+++ b/clang/test/OpenMP/target_parallel_for_simd_num_threads_messages.cpp
@@ -30,7 +30,7 @@ T tmain(T argc, S **argv) {
   for (i = 0; i < argc; ++i) foo();
   #pragma omp target parallel for simd num_threads (S) // expected-error {{'S' 
does not refer to a value}}
   for (i = 0; i < argc; ++i) foo();
-  #pragma omp target parallel for simd num_threads (argv[1]=2) // 
expected-error {{expected ')'}} expected-note {{to match this '('}} 
expected-error 2 {{expression must have integral or unscoped enumeration type, 
not 'char *'}}
+  #pragma omp target parallel for simd num_threads (argv[1]=2) // 
expected-error 2 {{incompatible integer to pointer conversion assigning to 
'char *' from 'int'}}
   for (i = 0; i < argc; ++i) foo();
   #pragma omp target parallel for simd num_threads (argc)
   for (i = 0; i < argc; ++i) foo();
@@ -58,7 +58,7 @@ int main(int argc, char **argv) {
   for (i = 0; i < argc; ++i) foo();
   #pragma omp target parallel for simd num_threads (S1) // expected-error 
{{'S1' does not refer to a value}}
   for (i = 0; i < argc; ++i) foo();
-  #pragma omp target parallel for simd num_threads (argv[1]=2) // 
expected-error {{expected ')'}} expected-note {{to match this '('}} 
expected-error {{expression must have integral or unscoped enumeration type, 
not 'char *'}}
+  #pragma omp target parallel for simd num_threads (argv[1]=2) // 
expected-error {{incompatible integer to pointer conversion assigning to 'char 
*' from 'int'}}
   for (i = 0; i < argc; ++i) foo();
   #pragma omp target parallel for simd num_threads (num_threads(tmain<int, 
char, -1>(argc, argv) // expected-error 2 {{expected ')'}} expected-note 2 {{to 
match this '('}} expected-note {{in instantiation of function template 
specialization 'tmain<int, char, -1>' requested here}}
   for (i = 0; i < argc; ++i) foo();
diff --git a/clang/test/OpenMP/target_parallel_num_threads_messages.cpp 
b/clang/test/OpenMP/target_parallel_num_threads_messages.cpp
index 64e9929d68101..19d40a3448cef 100644
--- a/clang/test/OpenMP/target_parallel_num_threads_messages.cpp
+++ b/clang/test/OpenMP/target_parallel_num_threads_messages.cpp
@@ -34,7 +34,7 @@ T tmain(T argc, S **argv) {
   foo();
   #pragma omp target parallel num_threads (S) // expected-error {{'S' does not 
refer to a value}}
   foo();
-  #pragma omp target parallel num_threads (argv[1]=2) // expected-error 
{{expected ')'}} expected-note {{to match this '('}} expected-error 2 
{{expression must have integral or unscoped enumeration type, not 'char *'}}
+  #pragma omp target parallel num_threads (argv[1]=2) // expected-error 2 
{{incompatible integer to pointer conversion assigning to 'char *' from 'int'}}
   foo();
   #pragma omp target parallel num_threads (argc + z)
   foo();
@@ -59,15 +59,15 @@ T tmain(T argc, S **argv) {
   foo();
 
   // Invalid: unknown/missing modifier
-  #pragma omp target parallel num_threads(foo: 4) // omp60-error {{expected 
'strict' in OpenMP clause 'num_threads'}}
+  #pragma omp target parallel num_threads(foo: 4) // omp60-error {{expected 
',' or ')' in 'num_threads' clause}} omp60-error {{expected ')'}} omp60-note 
{{to match this '('}} omp60-error 3 {{expression must have integral or unscoped 
enumeration type, not 'void ()'}}
   foo();
-  #pragma omp target parallel num_threads(: 4) // omp60-error {{expected 
expression}} omp60-error {{expected ')'}} omp60-note {{to match this '('}}
+  #pragma omp target parallel num_threads(: 4) // omp60-error {{expected 
expression}}
   foo();
-  #pragma omp target parallel num_threads(:)// omp60-error {{expected 
expression}} omp60-error {{expected ')'}} omp60-note {{to match this '('}}
+  #pragma omp target parallel num_threads(:)// omp60-error {{expected 
expression}}
   foo();
 
   // Invalid: missing colon after modifier
-  #pragma omp target parallel num_threads(strict 4) // omp60-error {{missing 
':' after strict modifier}}
+  #pragma omp target parallel num_threads(strict 4) // omp60-error {{missing 
':' after num_threads modifier}}
   foo();
 
   // Invalid: negative, zero, or non-integral
@@ -79,13 +79,13 @@ T tmain(T argc, S **argv) {
   foo();
   #pragma omp target parallel num_threads(strict: S) // omp60-error {{'S' does 
not refer to a value}}
   foo();
-  #pragma omp target parallel num_threads(strict: argv[1]=2) // omp60-error 
{{expected ')'}} omp60-note {{to match this '('}} omp60-error 2 {{expression 
must have integral or unscoped enumeration type, not 'char *'}}
+  #pragma omp target parallel num_threads(strict: argv[1]=2) // omp60-error 2 
{{incompatible integer to pointer conversion assigning to 'char *' from 'int'}}
   foo();
   #pragma omp target parallel num_threads(strict: N) // omp60-error {{argument 
to 'num_threads' clause must be a strictly positive integer value}}
   foo();
 
   // Invalid: multiple strict modifiers or mixed with non-strict
-  #pragma omp target parallel num_threads(strict: 4, strict: 5) // omp60-error 
{{expected ')'}} expected-note {{to match this '('}}
+  #pragma omp target parallel num_threads(strict: 4, strict: 5) // omp60-error 
{{use of undeclared identifier 'strict'}}
   foo();
   #pragma omp target parallel num_threads(strict: 4), num_threads(5) // 
omp60-error {{directive '#pragma omp target parallel' cannot contain more than 
one 'num_threads' clause}}
   foo();
@@ -114,7 +114,7 @@ int main(int argc, char **argv) {
   foo();
   #pragma omp target parallel num_threads (S1) // expected-error {{'S1' does 
not refer to a value}}
   foo();
-  #pragma omp target parallel num_threads (argv[1]=2) // expected-error 
{{expected ')'}} expected-note {{to match this '('}} expected-error 
{{expression must have integral or unscoped enumeration type, not 'char *'}}
+  #pragma omp target parallel num_threads (argv[1]=2) // expected-error 
{{incompatible integer to pointer conversion assigning to 'char *' from 'int'}}
   foo();
   #pragma omp target parallel num_threads (num_threads(tmain<int, char, 
-1>(argc, argv) // expected-error 2 {{expected ')'}} expected-note 2 {{to match 
this '('}} expected-note {{in instantiation of function template specialization 
'tmain<int, char, -1>' requested here}}
   foo();
@@ -137,15 +137,15 @@ int main(int argc, char **argv) {
   foo();
 
   // Invalid: unknown/missing modifier
-  #pragma omp target parallel num_threads(foo: 4) // omp60-error {{expected 
'strict' in OpenMP clause 'num_threads'}}
+  #pragma omp target parallel num_threads(foo: 4) // omp60-error {{expected 
',' or ')' in 'num_threads' clause}} omp60-error {{expected ')'}} omp60-note 
{{to match this '('}} omp60-error {{expression must have integral or unscoped 
enumeration type, not 'void ()'}}
   foo();
-  #pragma omp target parallel num_threads(: 4) // omp60-error {{expected 
expression}} omp60-error {{expected ')'}} omp60-note {{to match this '('}}
+  #pragma omp target parallel num_threads(: 4) // omp60-error {{expected 
expression}}
   foo();
-  #pragma omp target parallel num_threads(:) // omp60-error {{expected 
expression}} omp60-error {{expected ')'}} omp60-note {{to match this '('}}
+  #pragma omp target parallel num_threads(:) // omp60-error {{expected 
expression}}
   foo();
 
   // Invalid: missing colon after modifier
-  #pragma omp target parallel num_threads(strict 4) // omp60-error {{missing 
':' after strict modifier}}
+  #pragma omp target parallel num_threads(strict 4) // omp60-error {{missing 
':' after num_threads modifier}}
   foo();
 
   // Invalid: negative, zero, or non-integral
@@ -157,11 +157,11 @@ int main(int argc, char **argv) {
   foo();
   #pragma omp target parallel num_threads(strict: S1) // omp60-error {{'S1' 
does not refer to a value}}
   foo();
-  #pragma omp target parallel num_threads(strict: argv[1]=2) // omp60-error 
{{expected ')'}} omp60-note {{to match this '('}} omp60-error {{expression must 
have integral or unscoped enumeration type, not 'char *'}}
+  #pragma omp target parallel num_threads(strict: argv[1]=2) // omp60-error 
{{incompatible integer to pointer conversion assigning to 'char *' from 'int'}}
   foo();
 
   // Invalid: multiple strict modifiers or mixed with non-strict
-  #pragma omp target parallel num_threads(strict: 4, strict: 5) // omp60-error 
{{expected ')'}} expected-note {{to match this '('}}
+  #pragma omp target parallel num_threads(strict: 4, strict: 5) // omp60-error 
{{use of undeclared identifier 'strict'}}
   foo();
   #pragma omp target parallel num_threads(strict: 4), num_threads(5) // 
omp60-error {{directive '#pragma omp target parallel' cannot contain more than 
one 'num_threads' clause}}
   foo();
diff --git 
a/clang/test/OpenMP/target_teams_distribute_parallel_for_num_threads_messages.cpp
 
b/clang/test/OpenMP/target_teams_distribute_parallel_for_num_threads_messages.cpp
index d4b0c02c4393e..61690f0f052d6 100644
--- 
a/clang/test/OpenMP/target_teams_distribute_parallel_for_num_threads_messages.cpp
+++ 
b/clang/test/OpenMP/target_teams_distribute_parallel_for_num_threads_messages.cpp
@@ -30,7 +30,7 @@ T tmain(T argc, S **argv) {
   for (i = 0; i < argc; ++i) foo();
 #pragma omp target teams distribute parallel for num_threads (S) // 
expected-error {{'S' does not refer to a value}}
   for (i = 0; i < argc; ++i) foo();
-#pragma omp target teams distribute parallel for num_threads (argv[1]=2) // 
expected-error {{expected ')'}} expected-note {{to match this '('}} 
expected-error 2 {{expression must have integral or unscoped enumeration type, 
not 'char *'}}
+#pragma omp target teams distribute parallel for num_threads (argv[1]=2) // 
expected-error 2 {{incompatible integer to pointer conversion assigning to 
'char *' from 'int'}}
   for (i = 0; i < argc; ++i) foo();
 #pragma omp target teams distribute parallel for num_threads (argc)
   for (i = 0; i < argc; ++i) foo();
@@ -58,7 +58,7 @@ int main(int argc, char **argv) {
   for (i = 0; i < argc; ++i) foo();
 #pragma omp target teams distribute parallel for num_threads (S1) // 
expected-error {{'S1' does not refer to a value}}
   for (i = 0; i < argc; ++i) foo();
-#pragma omp target teams distribute parallel for num_threads (argv[1]=2) // 
expected-error {{expected ')'}} expected-note {{to match this '('}} 
expected-error {{expression must have integral or unscoped enumeration type, 
not 'char *'}}
+#pragma omp target teams distribute parallel for num_threads (argv[1]=2) // 
expected-error {{incompatible integer to pointer conversion assigning to 'char 
*' from 'int'}}
   for (i = 0; i < argc; ++i) foo();
 #pragma omp target teams distribute parallel for num_threads 
(num_threads(tmain<int, char, -1>(argc, argv) // expected-error 2 {{expected 
')'}} expected-note 2 {{to match this '('}} expected-note {{in instantiation of 
function template specialization 'tmain<int, char, -1>' requested here}}
   for (i = 0; i < argc; ++i) foo();
diff --git 
a/clang/test/OpenMP/target_teams_distribute_parallel_for_simd_num_threads_messages.cpp
 
b/clang/test/OpenMP/target_teams_distribute_parallel_for_simd_num_threads_messages.cpp
index 397c83d199e9e..017cdff2f28d9 100644
--- 
a/clang/test/OpenMP/target_teams_distribute_parallel_for_simd_num_threads_messages.cpp
+++ 
b/clang/test/OpenMP/target_teams_distribute_parallel_for_simd_num_threads_messages.cpp
@@ -30,7 +30,7 @@ T tmain(T argc, S **argv) {
   for (i = 0; i < argc; ++i) foo();
 #pragma omp target teams distribute parallel for simd num_threads (S) // 
expected-error {{'S' does not refer to a value}}
   for (i = 0; i < argc; ++i) foo();
-#pragma omp target teams distribute parallel for simd num_threads (argv[1]=2) 
// expected-error {{expected ')'}} expected-note {{to match this '('}} 
expected-error 2 {{expression must have integral or unscoped enumeration type, 
not 'char *'}}
+#pragma omp target teams distribute parallel for simd num_threads (argv[1]=2) 
// expected-error 2 {{incompatible integer to pointer conversion assigning to 
'char *' from 'int'}}
   for (i = 0; i < argc; ++i) foo();
 #pragma omp target teams distribute parallel for simd num_threads (argc + k)
   for (i = 0; i < argc; ++i) foo();
@@ -58,7 +58,7 @@ int main(int argc, char **argv) {
   for (i = 0; i < argc; ++i) foo();
 #pragma omp target teams distribute parallel for simd num_threads (S1) // 
expected-error {{'S1' does not refer to a value}}
   for (i = 0; i < argc; ++i) foo();
-#pragma omp target teams distribute parallel for simd num_threads (argv[1]=2) 
// expected-error {{expected ')'}} expected-note {{to match this '('}} 
expected-error {{expression must have integral or unscoped enumeration type, 
not 'char *'}}
+#pragma omp target teams distribute parallel for simd num_threads (argv[1]=2) 
// expected-error {{incompatible integer to pointer conversion assigning to 
'char *' from 'int'}}
   for (i = 0; i < argc; ++i) foo();
 #pragma omp target teams distribute parallel for simd num_threads 
(num_threads(tmain<int, char, -1>(argc, argv) // expected-error 2 {{expected 
')'}} expected-note 2 {{to match this '('}} expected-note {{in instantiation of 
function template specialization 'tmain<int, char, -1>' requested here}}
   for (i = 0; i < argc; ++i) foo();
diff --git 
a/clang/test/OpenMP/teams_distribute_parallel_for_simd_num_threads_messages.cpp 
b/clang/test/OpenMP/teams_distribute_parallel_for_simd_num_threads_messages.cpp
index 6ff5db319a985..4f24e7bd09356 100644
--- 
a/clang/test/OpenMP/teams_distribute_parallel_for_simd_num_threads_messages.cpp
+++ 
b/clang/test/OpenMP/teams_distribute_parallel_for_simd_num_threads_messages.cpp
@@ -39,7 +39,7 @@ T tmain(T argc, S **argv) {
 #pragma omp teams distribute parallel for simd num_threads (S) // 
expected-error {{'S' does not refer to a value}}
   for (i = 0; i < argc; ++i) foo();
 #pragma omp target
-#pragma omp teams distribute parallel for simd num_threads (argv[1]=2) // 
expected-error {{expected ')'}} expected-note {{to match this '('}} 
expected-error 2 {{expression must have integral or unscoped enumeration type, 
not 'char *'}}
+#pragma omp teams distribute parallel for simd num_threads (argv[1]=2) // 
expected-error 2 {{incompatible integer to pointer conversion assigning to 
'char *' from 'int'}}
   for (i = 0; i < argc; ++i) foo();
 #pragma omp target
 #pragma omp teams distribute parallel for simd num_threads (argc)
@@ -78,7 +78,7 @@ int main(int argc, char **argv) {
 #pragma omp teams distribute parallel for simd num_threads (S1) // 
expected-error {{'S1' does not refer to a value}}
   for (i = 0; i < argc; ++i) foo();
 #pragma omp target
-#pragma omp teams distribute parallel for simd num_threads (argv[1]=2) // 
expected-error {{expected ')'}} expected-note {{to match this '('}} 
expected-error {{expression must have integral or unscoped enumeration type, 
not 'char *'}}
+#pragma omp teams distribute parallel for simd num_threads (argv[1]=2) // 
expected-error {{incompatible integer to pointer conversion assigning to 'char 
*' from 'int'}}
   for (i = 0; i < argc; ++i) foo();
 #pragma omp target
 #pragma omp teams distribute parallel for simd num_threads 
(num_threads(tmain<int, char, -1>(argc, argv) // expected-error 2 {{expected 
')'}} expected-note 2 {{to match this '('}} expected-note {{in instantiation of 
function template specialization 'tmain<int, char, -1>' requested here}}
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index af9dc02656597..7a8f49e620ea9 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -2339,8 +2339,10 @@ void OMPClauseEnqueue::VisitOMPFinalClause(const 
OMPFinalClause *C) {
 }
 
 void OMPClauseEnqueue::VisitOMPNumThreadsClause(const OMPNumThreadsClause *C) {
+  if (const Expr *Modifier = C->getDimsModifierExpr())
+    Visitor->AddStmt(Modifier);
+  VisitOMPClauseList(C);
   VisitOMPClauseWithPreInit(C);
-  Visitor->AddStmt(C->getNumThreads());
 }
 
 void OMPClauseEnqueue::VisitOMPSafelenClause(const OMPSafelenClause *C) {

_______________________________________________
llvm-branch-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits

Reply via email to