https://github.com/zahiraam updated 
https://github.com/llvm/llvm-project/pull/236432

>From e5f4c70ec8dac8b5420c0febfb1e91c3d12e420f Mon Sep 17 00:00:00 2001
From: Zahira Ammarguellat <[email protected]>
Date: Wed, 28 Oct 2026 07:52:50 -0700
Subject: [PATCH 1/4] [OpenMP] Build the metadirective AST node for runtime
 selection

---
 clang/include/clang/AST/OpenMPClause.h        |  1 +
 clang/include/clang/AST/StmtOpenMP.h          | 81 +++++++++++++++----
 clang/include/clang/Sema/SemaOpenMP.h         |  9 +--
 clang/lib/AST/StmtOpenMP.cpp                  | 42 +++++++---
 clang/lib/AST/StmtPrinter.cpp                 | 33 +++++++-
 clang/lib/CodeGen/CGStmtOpenMP.cpp            |  3 +-
 clang/lib/Parse/ParseOpenMP.cpp               | 10 +--
 clang/lib/Sema/SemaOpenMP.cpp                 | 28 +++++--
 clang/lib/Serialization/ASTReaderStmt.cpp     |  5 +-
 clang/lib/Serialization/ASTWriterStmt.cpp     |  5 +-
 .../OpenMP/metadirective_runtime_ast_dump.cpp | 29 +++++++
 .../metadirective_runtime_ast_print.cpp       | 42 ++++++++++
 12 files changed, 239 insertions(+), 49 deletions(-)
 create mode 100644 clang/test/OpenMP/metadirective_runtime_ast_dump.cpp
 create mode 100644 clang/test/OpenMP/metadirective_runtime_ast_print.cpp

diff --git a/clang/include/clang/AST/OpenMPClause.h 
b/clang/include/clang/AST/OpenMPClause.h
index 960dcf65b2ca99..3fc2e696b1c2d0 100644
--- a/clang/include/clang/AST/OpenMPClause.h
+++ b/clang/include/clang/AST/OpenMPClause.h
@@ -10220,6 +10220,7 @@ class OMPChildren final
   friend TrailingObjects;
   friend class OMPClauseReader;
   friend class OMPExecutableDirective;
+  friend class OMPMetaDirective;
   template <typename T> friend class OMPDeclarativeDirective;
 
   /// Numbers of clauses.
diff --git a/clang/include/clang/AST/StmtOpenMP.h 
b/clang/include/clang/AST/StmtOpenMP.h
index a070d97a6db1f3..1503147d55cc0b 100644
--- a/clang/include/clang/AST/StmtOpenMP.h
+++ b/clang/include/clang/AST/StmtOpenMP.h
@@ -6553,32 +6553,85 @@ class OMPMaskedDirective final : public 
OMPExecutableDirective {
 /// #pragma omp metadirective when(user={condition(N>10)}: parallel for)
 /// \endcode
 /// In this example directive '#pragma omp metadirective' has clauses 'when'
-/// with a dynamic user condition to check if a variable 'N > 10'
+/// with a dynamic user condition to check if a variable 'N > 10'.
+/// The directive holds one variant per 'when' or 'otherwise' clause: the
+/// complete directive (or the plain associated statement), its condition and
+/// its context selectors.
 ///
 class OMPMetaDirective final : public OMPExecutableDirective {
   friend class ASTStmtReader;
   friend class OMPExecutableDirective;
-  Stmt *IfStmt;
+
+  /// True if the last variant is the implicit 'otherwise' added when the
+  /// directive has no 'otherwise' clause.
+  bool HasImplicitOtherwise = false;
 
   OMPMetaDirective(SourceLocation StartLoc, SourceLocation EndLoc)
       : OMPExecutableDirective(OMPMetaDirectiveClass,
                                llvm::omp::OMPD_metadirective, StartLoc,
                                EndLoc) {}
-  explicit OMPMetaDirective()
-      : OMPExecutableDirective(OMPMetaDirectiveClass,
-                               llvm::omp::OMPD_metadirective, SourceLocation(),
-                               SourceLocation()) {}
 
-  void setIfStmt(Stmt *S) { IfStmt = S; }
+  /// Layout: the children hold the variants, then their conditions; the
+  /// trait infos follow the children.
+  static size_t getTraitInfosOffset(unsigned NumVariants) {
+    return llvm::alignTo(OMPChildren::size(/*NumClauses=*/0,
+                                           /*HasAssociatedStmt=*/false,
+                                           2 * NumVariants),
+                         alignof(OMPTraitInfo *));
+  }
+  static OMPMetaDirective *allocate(const ASTContext &C, unsigned NumVariants,
+                                    SourceLocation StartLoc,
+                                    SourceLocation EndLoc);
+
+  MutableArrayRef<Stmt *> getTrailingVariants() {
+    return Data->getChildren().take_front(getNumVariants());
+  }
+  MutableArrayRef<Expr *> getTrailingConditions() {
+    return {reinterpret_cast<Expr **>(Data->getChildren().data() +
+                                      getNumVariants()),
+            getNumVariants()};
+  }
+  MutableArrayRef<OMPTraitInfo *> getTrailingTraitInfos() {
+    return {reinterpret_cast<OMPTraitInfo **>(
+                reinterpret_cast<char *>(Data) +
+                getTraitInfosOffset(getNumVariants())),
+            getNumVariants()};
+  }
+  ArrayRef<Stmt *> getTrailingVariants() const {
+    return const_cast<OMPMetaDirective *>(this)->getTrailingVariants();
+  }
+  ArrayRef<Expr *> getTrailingConditions() const {
+    return const_cast<OMPMetaDirective *>(this)->getTrailingConditions();
+  }
+  ArrayRef<OMPTraitInfo *> getTrailingTraitInfos() const {
+    return const_cast<OMPMetaDirective *>(this)->getTrailingTraitInfos();
+  }
+
+  void setHasImplicitOtherwise(bool V) { HasImplicitOtherwise = V; }
 
 public:
-  static OMPMetaDirective *Create(const ASTContext &C, SourceLocation StartLoc,
-                                  SourceLocation EndLoc,
-                                  ArrayRef<OMPClause *> Clauses,
-                                  Stmt *AssociatedStmt, Stmt *IfStmt);
-  static OMPMetaDirective *CreateEmpty(const ASTContext &C, unsigned 
NumClauses,
-                                       EmptyShell);
-  Stmt *getIfStmt() const { return IfStmt; }
+  static OMPMetaDirective *
+  Create(const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc,
+         ArrayRef<Stmt *> Variants, ArrayRef<Expr *> Conditions,
+         ArrayRef<OMPTraitInfo *> TraitInfos, bool HasImplicitOtherwise);
+  static OMPMetaDirective *CreateEmpty(const ASTContext &C,
+                                       unsigned NumVariants, EmptyShell);
+
+  unsigned getNumVariants() const { return Data->getChildren().size() / 2; }
+  bool hasImplicitOtherwise() const { return HasImplicitOtherwise; }
+  ArrayRef<Stmt *> variants() const { return getTrailingVariants(); }
+  ArrayRef<Expr *> conditions() const { return getTrailingConditions(); }
+  ArrayRef<OMPTraitInfo *> traitInfos() const {
+    return getTrailingTraitInfos();
+  }
+
+  child_range children() {
+    MutableArrayRef<Stmt *> Children = Data->getChildren();
+    return child_range(Children.begin(), Children.end());
+  }
+  const_child_range children() const {
+    return const_cast<OMPMetaDirective *>(this)->children();
+  }
 
   static bool classof(const Stmt *T) {
     return T->getStmtClass() == OMPMetaDirectiveClass;
diff --git a/clang/include/clang/Sema/SemaOpenMP.h 
b/clang/include/clang/Sema/SemaOpenMP.h
index 49d504e31bdb87..010ea105e609ba 100644
--- a/clang/include/clang/Sema/SemaOpenMP.h
+++ b/clang/include/clang/Sema/SemaOpenMP.h
@@ -211,19 +211,14 @@ class SemaOpenMP : public SemaBase {
   /// \param Init First part of the for loop.
   void ActOnOpenMPLoopInitialization(SourceLocation ForLoc, Stmt *Init);
 
-  /// Called on well-formed '\#pragma omp metadirective' after parsing
-  /// of the  associated statement.
-  StmtResult ActOnOpenMPMetaDirective(ArrayRef<OMPClause *> Clauses,
-                                      Stmt *AStmt, SourceLocation StartLoc,
-                                      SourceLocation EndLoc);
-
   /// Called for metadirectives with user conditions that require runtime
   /// selection. \p Variants holds one complete directive (or the plain
   /// associated statement) per entry of \p TraitInfos.
   StmtResult ActOnOpenMPMetaDirective(SourceLocation StartLoc,
                                       SourceLocation EndLoc,
                                       ArrayRef<OMPTraitInfo *> TraitInfos,
-                                      ArrayRef<Stmt *> Variants);
+                                      ArrayRef<Stmt *> Variants,
+                                      bool HasImplicitOtherwise);
 
   // OpenMP directives and clauses.
   /// Called on correct id-expression from the '#pragma omp
diff --git a/clang/lib/AST/StmtOpenMP.cpp b/clang/lib/AST/StmtOpenMP.cpp
index 5ba11b2d8c42fa..9a20b8273c3288 100644
--- a/clang/lib/AST/StmtOpenMP.cpp
+++ b/clang/lib/AST/StmtOpenMP.cpp
@@ -331,23 +331,41 @@ void OMPLoopDirective::setFinalsConditions(ArrayRef<Expr 
*> A) {
   llvm::copy(A, getFinalsConditions().begin());
 }
 
-OMPMetaDirective *OMPMetaDirective::Create(const ASTContext &C,
-                                           SourceLocation StartLoc,
-                                           SourceLocation EndLoc,
-                                           ArrayRef<OMPClause *> Clauses,
-                                           Stmt *AssociatedStmt, Stmt *IfStmt) 
{
-  auto *Dir = createDirective<OMPMetaDirective>(
-      C, Clauses, AssociatedStmt, /*NumChildren=*/1, StartLoc, EndLoc);
-  Dir->setIfStmt(IfStmt);
+OMPMetaDirective *OMPMetaDirective::allocate(const ASTContext &C,
+                                             unsigned NumVariants,
+                                             SourceLocation StartLoc,
+                                             SourceLocation EndLoc) {
+  void *Mem =
+      C.Allocate(sizeof(OMPMetaDirective) + getTraitInfosOffset(NumVariants) +
+                     NumVariants * sizeof(OMPTraitInfo *),
+                 alignof(OMPMetaDirective));
+  auto *Data = OMPChildren::CreateEmpty(
+      reinterpret_cast<OMPMetaDirective *>(Mem) + 1, /*NumClauses=*/0,
+      /*HasAssociatedStmt=*/false, 2 * NumVariants);
+  auto *Dir = new (Mem) OMPMetaDirective(StartLoc, EndLoc);
+  Dir->Data = Data;
+  return Dir;
+}
+
+OMPMetaDirective *OMPMetaDirective::Create(
+    const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc,
+    ArrayRef<Stmt *> Variants, ArrayRef<Expr *> Conditions,
+    ArrayRef<OMPTraitInfo *> TraitInfos, bool HasImplicitOtherwise) {
+  assert(Variants.size() == Conditions.size() &&
+         Variants.size() == TraitInfos.size() &&
+         "Expected one condition and one trait info per variant");
+  OMPMetaDirective *Dir = allocate(C, Variants.size(), StartLoc, EndLoc);
+  llvm::copy(Variants, Dir->getTrailingVariants().begin());
+  llvm::copy(Conditions, Dir->getTrailingConditions().begin());
+  llvm::copy(TraitInfos, Dir->getTrailingTraitInfos().begin());
+  Dir->setHasImplicitOtherwise(HasImplicitOtherwise);
   return Dir;
 }
 
 OMPMetaDirective *OMPMetaDirective::CreateEmpty(const ASTContext &C,
-                                                unsigned NumClauses,
+                                                unsigned NumVariants,
                                                 EmptyShell) {
-  return createEmptyDirective<OMPMetaDirective>(C, NumClauses,
-                                                /*HasAssociatedStmt=*/true,
-                                                /*NumChildren=*/1);
+  return allocate(C, NumVariants, SourceLocation(), SourceLocation());
 }
 
 OMPParallelDirective *OMPParallelDirective::Create(
diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index 4f184868659f48..0c4b9e7dd76cc5 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -796,7 +796,38 @@ void 
StmtPrinter::PrintOMPExecutableDirective(OMPExecutableDirective *S,
 
 void StmtPrinter::VisitOMPMetaDirective(OMPMetaDirective *Node) {
   Indent() << "#pragma omp metadirective";
-  PrintOMPExecutableDirective(Node);
+  llvm::omp::Version OpenMPVersion =
+      Context ? Context->getLangOpts().getOpenMPVersion()
+              : llvm::omp::FallbackVersion;
+  OMPClausePrinter Printer(OS, Policy, OpenMPVersion);
+  ArrayRef<Stmt *> Variants = Node->variants();
+  ArrayRef<OMPTraitInfo *> TraitInfos = Node->traitInfos();
+  unsigned NumClauses =
+      Node->getNumVariants() - (Node->hasImplicitOtherwise() ? 1 : 0);
+  for (unsigned I = 0; I != NumClauses; ++I) {
+    if (TraitInfos[I]->Sets.empty()) {
+      OS << (OpenMPVersion < 52 ? " default(" : " otherwise(");
+    } else {
+      OS << " when(";
+      TraitInfos[I]->print(OS, Policy);
+      OS << ": ";
+    }
+    if (auto *D = dyn_cast<OMPExecutableDirective>(Variants[I])) {
+      OS << getOpenMPDirectiveName(D->getDirectiveKind(), OpenMPVersion);
+      for (OMPClause *C : D->clauses())
+        if (C && !C->isImplicit()) {
+          OS << ' ';
+          Printer.Visit(C);
+        }
+    }
+    OS << ')';
+  }
+  OS << NL;
+  // Every variant has the same associated statement; print it once.
+  Stmt *Body = Variants.front();
+  if (auto *D = dyn_cast<OMPExecutableDirective>(Body))
+    Body = D->getStructuredBlock();
+  PrintStmt(Body);
 }
 
 void StmtPrinter::VisitOMPParallelDirective(OMPParallelDirective *Node) {
diff --git a/clang/lib/CodeGen/CGStmtOpenMP.cpp 
b/clang/lib/CodeGen/CGStmtOpenMP.cpp
index e63957df3e4069..20a0abd7f86328 100644
--- a/clang/lib/CodeGen/CGStmtOpenMP.cpp
+++ b/clang/lib/CodeGen/CGStmtOpenMP.cpp
@@ -2398,7 +2398,8 @@ void CodeGenFunction::EmitOMPParallelDirective(const 
OMPParallelDirective &S) {
 }
 
 void CodeGenFunction::EmitOMPMetaDirective(const OMPMetaDirective &S) {
-  EmitStmt(S.getIfStmt());
+  // TODO: Select the variant at runtime.
+  EmitStmt(S.variants().front());
 }
 
 namespace {
diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp
index 1e3e400cc7d055..66cb59ce196d43 100644
--- a/clang/lib/Parse/ParseOpenMP.cpp
+++ b/clang/lib/Parse/ParseOpenMP.cpp
@@ -2736,9 +2736,9 @@ StmtResult 
Parser::ParseOpenMPDeclarativeOrExecutableDirective(
       // was specified without an associated directive variant. Only
       // 'otherwise' (or the deprecated 'default') has no context selectors.
       unsigned NumClauses = TraitInfos.size();
-      if (llvm::none_of(TraitInfos, [](const OMPTraitInfo *TI) {
-            return TI->Sets.empty();
-          }))
+      bool HasImplicitOtherwise = llvm::none_of(
+          TraitInfos, [](const OMPTraitInfo *TI) { return TI->Sets.empty(); });
+      if (HasImplicitOtherwise)
         TraitInfos.push_back(&ASTContext.getNewOMPTraitInfo());
 
       // Reuse the normal metadirective variant parse per clause so every
@@ -2802,8 +2802,8 @@ StmtResult 
Parser::ParseOpenMPDeclarativeOrExecutableDirective(
       }
       if (HasError)
         return StmtError();
-      return Actions.OpenMP().ActOnOpenMPMetaDirective(Loc, EndLoc, TraitInfos,
-                                                       Variants);
+      return Actions.OpenMP().ActOnOpenMPMetaDirective(
+          Loc, EndLoc, TraitInfos, Variants, HasImplicitOtherwise);
     }
 
     int Idx = 0;
diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index 5fba2895938703..d56a8b4b385f4e 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -3845,15 +3845,29 @@ StmtResult 
SemaOpenMP::ActOnOpenMPAssumeDirective(ArrayRef<OMPClause *> Clauses,
 
 StmtResult SemaOpenMP::ActOnOpenMPMetaDirective(
     SourceLocation StartLoc, SourceLocation EndLoc,
-    ArrayRef<OMPTraitInfo *> TraitInfos, ArrayRef<Stmt *> Variants) {
+    ArrayRef<OMPTraitInfo *> TraitInfos, ArrayRef<Stmt *> Variants,
+    bool HasImplicitOtherwise) {
   assert(TraitInfos.size() == Variants.size() &&
          "Expected one variant per metadirective clause");
-  // TODO Phase 3: Build OMPMetaDirective AST node holding all variants.
-  // TODO Phase 4: Codegen - generate runtime if-else chain.
-  // Temporary stub: return the first variant until OMPMetaDirective exists.
-  if (Variants.empty())
-    return StmtError();
-  return Variants.front();
+  // The user condition of each variant; null for 'otherwise'.
+  SmallVector<Expr *, 4> Conditions;
+  for (OMPTraitInfo *TI : TraitInfos) {
+    Expr *Cond = nullptr;
+    TI->anyScoreOrCondition([&](Expr *&E, bool IsScore) {
+      if (!IsScore)
+        Cond = E;
+      return !IsScore;
+    });
+    if (Cond) {
+      ExprResult Val = SemaRef.CheckBooleanCondition(Cond->getExprLoc(), Cond);
+      if (Val.isInvalid())
+        return StmtError();
+      Cond = SemaRef.MakeFullExpr(Val.get()).get();
+    }
+    Conditions.push_back(Cond);
+  }
+  return OMPMetaDirective::Create(getASTContext(), StartLoc, EndLoc, Variants,
+                                  Conditions, TraitInfos, 
HasImplicitOtherwise);
 }
 
 OMPRequiresDecl *
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp 
b/clang/lib/Serialization/ASTReaderStmt.cpp
index 26aee27c65e82f..b09471c13a48e2 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -2541,9 +2541,12 @@ void 
ASTStmtReader::VisitOMPLoopDirective(OMPLoopDirective *D) {
 
 void ASTStmtReader::VisitOMPMetaDirective(OMPMetaDirective *D) {
   VisitStmt(D);
-  // The NumClauses field was read in ReadStmtFromStream.
+  // The NumVariants field was read in ReadStmtFromStream.
   Record.skipInts(1);
   VisitOMPExecutableDirective(D);
+  D->setHasImplicitOtherwise(Record.readBool());
+  for (OMPTraitInfo *&TI : D->getTrailingTraitInfos())
+    TI = Record.readOMPTraitInfo();
 }
 
 void ASTStmtReader::VisitOMPParallelDirective(OMPParallelDirective *D) {
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp 
b/clang/lib/Serialization/ASTWriterStmt.cpp
index c6f04336f806ee..00e79847c4f5ce 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -2613,8 +2613,11 @@ void 
ASTStmtWriter::VisitOMPLoopDirective(OMPLoopDirective *D) {
 
 void ASTStmtWriter::VisitOMPMetaDirective(OMPMetaDirective *D) {
   VisitStmt(D);
-  Record.push_back(D->getNumClauses());
+  Record.push_back(D->getNumVariants());
   VisitOMPExecutableDirective(D);
+  Record.writeBool(D->hasImplicitOtherwise());
+  for (const OMPTraitInfo *TI : D->traitInfos())
+    Record.writeOMPTraitInfo(TI);
   Code = serialization::STMT_OMP_META_DIRECTIVE;
 }
 
diff --git a/clang/test/OpenMP/metadirective_runtime_ast_dump.cpp 
b/clang/test/OpenMP/metadirective_runtime_ast_dump.cpp
new file mode 100644
index 00000000000000..d41aa6ea83749f
--- /dev/null
+++ b/clang/test/OpenMP/metadirective_runtime_ast_dump.cpp
@@ -0,0 +1,29 @@
+// RUN: %clang_cc1 -fopenmp -fopenmp-version=52 -std=c++11 \
+// RUN:   -fopenmp-metadirective-runtime-selection -ast-dump %s | FileCheck %s
+
+// CHECK-LABEL: FunctionDecl {{.*}} when_otherwise
+// CHECK:      OMPMetaDirective
+// CHECK-NEXT: OMPParallelDirective
+// CHECK:      OMPSingleDirective
+// CHECK:      ImplicitCastExpr {{.*}} 'bool' <IntegralToBoolean>
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'flag' 'int'
+// CHECK-NEXT: <<<NULL>>>
+void when_otherwise(int flag) {
+#pragma omp metadirective when(user={condition(flag)}: parallel) 
otherwise(single)
+  {
+  }
+}
+
+// Without 'otherwise', the last variant is the plain associated statement.
+// CHECK-LABEL: FunctionDecl {{.*}} no_otherwise
+// CHECK:      OMPMetaDirective
+// CHECK-NEXT: OMPParallelDirective
+// CHECK:      {{^ *}}|-CompoundStmt
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'bool' <IntegralToBoolean>
+// CHECK:      <<<NULL>>>
+void no_otherwise(int flag) {
+#pragma omp metadirective when(user={condition(flag)}: parallel)
+  {
+  }
+}
diff --git a/clang/test/OpenMP/metadirective_runtime_ast_print.cpp 
b/clang/test/OpenMP/metadirective_runtime_ast_print.cpp
new file mode 100644
index 00000000000000..e41cd65b15a9e8
--- /dev/null
+++ b/clang/test/OpenMP/metadirective_runtime_ast_print.cpp
@@ -0,0 +1,42 @@
+// RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=52 -std=c++11 \
+// RUN:   -fopenmp-metadirective-runtime-selection -ast-print %s \
+// RUN:   | FileCheck %s
+// RUN: %clang_cc1 -fopenmp -fopenmp-version=52 -std=c++11 \
+// RUN:   -fopenmp-metadirective-runtime-selection -x c++ -emit-pch -o %t %s
+// RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=52 -std=c++11 \
+// RUN:   -fopenmp-metadirective-runtime-selection -include-pch %t \
+// RUN:   -ast-print %s | FileCheck %s
+
+// expected-no-diagnostics
+
+#ifndef HEADER
+#define HEADER
+
+// CHECK-LABEL: void when_otherwise(int flag) {
+// CHECK-NEXT: #pragma omp metadirective when(user={condition(flag)}: 
parallel) otherwise(single)
+// CHECK-NEXT: {
+void when_otherwise(int flag) {
+#pragma omp metadirective when(user={condition(flag)}: parallel) 
otherwise(single)
+  {
+  }
+}
+
+// The implicit 'otherwise' is not printed.
+// CHECK-LABEL: void no_otherwise(int flag) {
+// CHECK-NEXT: #pragma omp metadirective when(user={condition(flag)}: 
parallel){{$}}
+void no_otherwise(int flag) {
+#pragma omp metadirective when(user={condition(flag)}: parallel)
+  {
+  }
+}
+
+// Variant clauses and an empty variant are printed as written.
+// CHECK-LABEL: void clauses(int flag) {
+// CHECK-NEXT: #pragma omp metadirective when(user={condition(flag)}: parallel 
num_threads(4)) when(user={condition(!flag)}: )
+void clauses(int flag) {
+#pragma omp metadirective when(user={condition(flag)}: parallel 
num_threads(4)) when(user={condition(!flag)}: )
+  {
+  }
+}
+
+#endif

>From 581fc1e5751298f73313aecec5e3d9bdb8bca4ad Mon Sep 17 00:00:00 2001
From: Zahira Ammarguellat <[email protected]>
Date: Wed, 28 Oct 2026 08:08:34 -0700
Subject: [PATCH 2/4] Added LIT test for CFG limitation

---
 .../OpenMP/metadirective_runtime_uninit.cpp   | 23 +++++++++++++++++++
 1 file changed, 23 insertions(+)
 create mode 100644 clang/test/OpenMP/metadirective_runtime_uninit.cpp

diff --git a/clang/test/OpenMP/metadirective_runtime_uninit.cpp 
b/clang/test/OpenMP/metadirective_runtime_uninit.cpp
new file mode 100644
index 00000000000000..cd519142cc991d
--- /dev/null
+++ b/clang/test/OpenMP/metadirective_runtime_uninit.cpp
@@ -0,0 +1,23 @@
+// RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=52 -std=c++11 \
+// RUN:   -fopenmp-metadirective-runtime-selection -Wuninitialized \
+// RUN:   -fsyntax-only %s
+
+void use(int);
+
+void plain_parallel() {
+  int x; // expected-note {{initialize the variable 'x' to silence this 
warning}}
+#pragma omp parallel
+  {
+    use(x); // expected-warning {{variable 'x' is uninitialized when used 
here}}
+  }
+}
+
+// FIXME: Known limitation, to be fixed in a follow-up: CFG-based analyses
+// skip the variants, so this does not warn.
+void runtime_metadirective(int flag) {
+  int x;
+#pragma omp metadirective when(user={condition(flag)}: parallel) 
otherwise(single)
+  {
+    use(x);
+  }
+}

>From cfd39798febd83a205af66cf9c49e2cb12e0f8d5 Mon Sep 17 00:00:00 2001
From: Zahira Ammarguellat <[email protected]>
Date: Wed, 28 Oct 2026 08:16:06 -0700
Subject: [PATCH 3/4] Added another warning flag

---
 ...> metadirective_runtime_flow_warnings.cpp} | 21 ++++++++++++++++++-
 1 file changed, 20 insertions(+), 1 deletion(-)
 rename clang/test/OpenMP/{metadirective_runtime_uninit.cpp => 
metadirective_runtime_flow_warnings.cpp} (56%)

diff --git a/clang/test/OpenMP/metadirective_runtime_uninit.cpp 
b/clang/test/OpenMP/metadirective_runtime_flow_warnings.cpp
similarity index 56%
rename from clang/test/OpenMP/metadirective_runtime_uninit.cpp
rename to clang/test/OpenMP/metadirective_runtime_flow_warnings.cpp
index cd519142cc991d..a8cb07041fcacc 100644
--- a/clang/test/OpenMP/metadirective_runtime_uninit.cpp
+++ b/clang/test/OpenMP/metadirective_runtime_flow_warnings.cpp
@@ -1,7 +1,8 @@
 // RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=52 -std=c++11 \
 // RUN:   -fopenmp-metadirective-runtime-selection -Wuninitialized \
-// RUN:   -fsyntax-only %s
+// RUN:   -Wunreachable-code -fsyntax-only %s
 
+[[noreturn]] void stop();
 void use(int);
 
 void plain_parallel() {
@@ -21,3 +22,21 @@ void runtime_metadirective(int flag) {
     use(x);
   }
 }
+
+void unreachable_parallel() {
+#pragma omp parallel
+  {
+    stop();
+    use(1); // expected-warning {{code will never be executed}}
+  }
+}
+
+// FIXME: Known limitation, to be fixed in a follow-up: CFG-based analyses
+// skip the variants, so this does not warn.
+void unreachable_metadirective(int flag) {
+#pragma omp metadirective when(user={condition(flag)}: parallel) 
otherwise(single)
+  {
+    stop();
+    use(1);
+  }
+}

>From cf0660529a42c9edc1cfa4e416203835df4a2ffc Mon Sep 17 00:00:00 2001
From: Zahira Ammarguellat <[email protected]>
Date: Mon, 2 Nov 2026 06:45:44 -0800
Subject: [PATCH 4/4] Moved code from Parser to Sema

---
 clang/include/clang/Sema/SemaOpenMP.h |  3 +--
 clang/lib/Parse/ParseOpenMP.cpp       | 19 +++++++++++--------
 clang/lib/Sema/SemaOpenMP.cpp         | 15 ++++++++++-----
 3 files changed, 22 insertions(+), 15 deletions(-)

diff --git a/clang/include/clang/Sema/SemaOpenMP.h 
b/clang/include/clang/Sema/SemaOpenMP.h
index 010ea105e609ba..16f2c8269943c2 100644
--- a/clang/include/clang/Sema/SemaOpenMP.h
+++ b/clang/include/clang/Sema/SemaOpenMP.h
@@ -217,8 +217,7 @@ class SemaOpenMP : public SemaBase {
   StmtResult ActOnOpenMPMetaDirective(SourceLocation StartLoc,
                                       SourceLocation EndLoc,
                                       ArrayRef<OMPTraitInfo *> TraitInfos,
-                                      ArrayRef<Stmt *> Variants,
-                                      bool HasImplicitOtherwise);
+                                      ArrayRef<Stmt *> Variants);
 
   // OpenMP directives and clauses.
   /// Called on correct id-expression from the '#pragma omp
diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp
index 66cb59ce196d43..878eb3f2d3058b 100644
--- a/clang/lib/Parse/ParseOpenMP.cpp
+++ b/clang/lib/Parse/ParseOpenMP.cpp
@@ -2736,10 +2736,13 @@ StmtResult 
Parser::ParseOpenMPDeclarativeOrExecutableDirective(
       // was specified without an associated directive variant. Only
       // 'otherwise' (or the deprecated 'default') has no context selectors.
       unsigned NumClauses = TraitInfos.size();
-      bool HasImplicitOtherwise = llvm::none_of(
-          TraitInfos, [](const OMPTraitInfo *TI) { return TI->Sets.empty(); });
-      if (HasImplicitOtherwise)
-        TraitInfos.push_back(&ASTContext.getNewOMPTraitInfo());
+      unsigned NumVariants =
+          NumClauses + (llvm::none_of(TraitInfos,
+                                      [](const OMPTraitInfo *TI) {
+                                        return TI->Sets.empty();
+                                      })
+                            ? 1
+                            : 0);
 
       // Reuse the normal metadirective variant parse per clause so every
       // variant is a complete directive node with its own body and its own
@@ -2747,7 +2750,7 @@ StmtResult 
Parser::ParseOpenMPDeclarativeOrExecutableDirective(
       // reported once per variant.
       SmallVector<Stmt *, 4> Variants;
       bool HasError = false;
-      for (unsigned I : llvm::seq<unsigned>(TraitInfos.size())) {
+      for (unsigned I : llvm::seq<unsigned>(NumVariants)) {
         bool HasDirective = false;
         if (I < NumClauses) {
           OpenMPClauseKind CKind = getOpenMPClauseKind(PP.getSpelling(Tok));
@@ -2764,7 +2767,7 @@ StmtResult 
Parser::ParseOpenMPDeclarativeOrExecutableDirective(
         }
         // Re-parse the directive and body for every variant but the last.
         std::optional<TentativeParsingAction> VariantTPA;
-        if (I + 1 < TraitInfos.size())
+        if (I + 1 < NumVariants)
           VariantTPA.emplace(*this, /*Unannotated=*/true);
 
         StmtResult Variant;
@@ -2802,8 +2805,8 @@ StmtResult 
Parser::ParseOpenMPDeclarativeOrExecutableDirective(
       }
       if (HasError)
         return StmtError();
-      return Actions.OpenMP().ActOnOpenMPMetaDirective(
-          Loc, EndLoc, TraitInfos, Variants, HasImplicitOtherwise);
+      return Actions.OpenMP().ActOnOpenMPMetaDirective(Loc, EndLoc, TraitInfos,
+                                                       Variants);
     }
 
     int Idx = 0;
diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index d56a8b4b385f4e..b500fbdda6a262 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -3845,13 +3845,17 @@ StmtResult 
SemaOpenMP::ActOnOpenMPAssumeDirective(ArrayRef<OMPClause *> Clauses,
 
 StmtResult SemaOpenMP::ActOnOpenMPMetaDirective(
     SourceLocation StartLoc, SourceLocation EndLoc,
-    ArrayRef<OMPTraitInfo *> TraitInfos, ArrayRef<Stmt *> Variants,
-    bool HasImplicitOtherwise) {
-  assert(TraitInfos.size() == Variants.size() &&
+    ArrayRef<OMPTraitInfo *> TraitInfos, ArrayRef<Stmt *> Variants) {
+  // Without an 'otherwise' clause, the last variant is the implicit one.
+  bool HasImplicitOtherwise = Variants.size() == TraitInfos.size() + 1;
+  assert((HasImplicitOtherwise || Variants.size() == TraitInfos.size()) &&
          "Expected one variant per metadirective clause");
+  SmallVector<OMPTraitInfo *, 4> AllTraitInfos(TraitInfos);
+  if (HasImplicitOtherwise)
+    AllTraitInfos.push_back(&getASTContext().getNewOMPTraitInfo());
   // The user condition of each variant; null for 'otherwise'.
   SmallVector<Expr *, 4> Conditions;
-  for (OMPTraitInfo *TI : TraitInfos) {
+  for (OMPTraitInfo *TI : AllTraitInfos) {
     Expr *Cond = nullptr;
     TI->anyScoreOrCondition([&](Expr *&E, bool IsScore) {
       if (!IsScore)
@@ -3867,7 +3871,8 @@ StmtResult SemaOpenMP::ActOnOpenMPMetaDirective(
     Conditions.push_back(Cond);
   }
   return OMPMetaDirective::Create(getASTContext(), StartLoc, EndLoc, Variants,
-                                  Conditions, TraitInfos, 
HasImplicitOtherwise);
+                                  Conditions, AllTraitInfos,
+                                  HasImplicitOtherwise);
 }
 
 OMPRequiresDecl *

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

Reply via email to