[clang] [llvm] [OpenMP] Add runtime selection for metadirective with non-constant conditions. (PR #192455)
Zahira Ammarguellat via cfe-commits
cfe-commits at lists.llvm.org
Tue Sep 8 07:48:29 PDT 2026
https://github.com/zahiraam updated https://github.com/llvm/llvm-project/pull/192455
>From a8a2ba6bb8c246f0b2b0bf57b871d98e4bc597e6 Mon Sep 17 00:00:00 2001
From: Zahira Ammarguellat <zahira.ammarguellat at intel.com>
Date: Mon, 17 Aug 2026 14:21:21 -0700
Subject: [PATCH 1/5] Implement phase 1 of plan
---
clang/include/clang/AST/OpenMPClause.h | 13 ++
clang/include/clang/Sema/SemaOpenMP.h | 8 ++
clang/lib/AST/OpenMPClause.cpp | 4 +-
clang/lib/Parse/ParseOpenMP.cpp | 91 +++++++++++-
clang/lib/Sema/SemaOpenMP.cpp | 9 ++
.../metadirective_user_condition_parse.cpp | 134 ++++++++++++++++++
.../include/llvm/Frontend/OpenMP/OMPContext.h | 4 +
7 files changed, 260 insertions(+), 3 deletions(-)
create mode 100644 clang/test/OpenMP/metadirective_user_condition_parse.cpp
diff --git a/clang/include/clang/AST/OpenMPClause.h b/clang/include/clang/AST/OpenMPClause.h
index ec84f10956ff4..c39af8d3148f8 100644
--- a/clang/include/clang/AST/OpenMPClause.h
+++ b/clang/include/clang/AST/OpenMPClause.h
@@ -10129,6 +10129,19 @@ class OMPTraitInfo {
return false;
}
+ /// Check if this trait info contains any user conditions.
+ bool hasUserCondition() const {
+ for (const OMPTraitSet &Set : Sets) {
+ if (Set.Kind != llvm::omp::TraitSet::user)
+ continue;
+ for (const OMPTraitSelector &Selector : Set.Selectors) {
+ if (Selector.Kind == llvm::omp::TraitSelector::user_condition)
+ return true;
+ }
+ }
+ return false;
+ }
+
/// Print a human readable representation into \p OS.
void print(llvm::raw_ostream &OS, const PrintingPolicy &Policy) const;
};
diff --git a/clang/include/clang/Sema/SemaOpenMP.h b/clang/include/clang/Sema/SemaOpenMP.h
index a5f357c15f5c4..71a43da569de6 100644
--- a/clang/include/clang/Sema/SemaOpenMP.h
+++ b/clang/include/clang/Sema/SemaOpenMP.h
@@ -217,6 +217,14 @@ class SemaOpenMP : public SemaBase {
Stmt *AStmt, SourceLocation StartLoc,
SourceLocation EndLoc);
+ /// Called for metadirectives with user conditions that may require runtime
+ /// selection.
+ StmtResult ActOnOpenMPMetaDirective(
+ SourceLocation StartLoc, SourceLocation EndLoc,
+ ArrayRef<OMPTraitInfo *> TraitInfos,
+ ArrayRef<OpenMPClauseKind> ClauseKinds,
+ ArrayRef<OpenMPDirectiveKind> DirectiveKinds, Stmt *AssociatedStmt);
+
// OpenMP directives and clauses.
/// Called on correct id-expression from the '#pragma omp
/// threadprivate'.
diff --git a/clang/lib/AST/OpenMPClause.cpp b/clang/lib/AST/OpenMPClause.cpp
index 2061d5395ac65..1a4c03f3bf1b1 100644
--- a/clang/lib/AST/OpenMPClause.cpp
+++ b/clang/lib/AST/OpenMPClause.cpp
@@ -3131,8 +3131,10 @@ void OMPTraitInfo::getAsVariantMatchInfo(ASTContext &ASTCtx,
VMI.addTrait(CondVal->isZero() ? TraitProperty::user_condition_false
: TraitProperty::user_condition_true,
"<condition>");
- else
+ else {
VMI.addTrait(TraitProperty::user_condition_false, "<condition>");
+ VMI.HasNonConstantUserCondition = true;
+ }
continue;
}
diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp
index 0ce484c5e907d..1f2c48f17e9f1 100644
--- a/clang/lib/Parse/ParseOpenMP.cpp
+++ b/clang/lib/Parse/ParseOpenMP.cpp
@@ -108,6 +108,22 @@ static OpenMPDirectiveKind parseOpenMPDirectiveKind(Parser &P) {
return checkOpenMPDirectiveName(P, Loc, S->Value, Concat);
}
+/// Skip tokens until reaching the matching closing parenthesis.
+/// Handles nested parentheses correctly.
+static void skipToMatchingParen(Parser &P) {
+ int ParenDepth = 0;
+ while ((P.getCurToken().isNot(tok::r_paren) || ParenDepth != 0) &&
+ P.getCurToken().isNot(tok::annot_pragma_openmp_end) &&
+ P.getCurToken().isNot(tok::eof)) {
+ if (P.getCurToken().is(tok::l_paren))
+ ParenDepth++;
+ if (P.getCurToken().is(tok::r_paren) && ParenDepth > 0)
+ ParenDepth--;
+ if (ParenDepth > 0 || P.getCurToken().isNot(tok::r_paren))
+ P.ConsumeAnyToken();
+ }
+}
+
static DeclarationName parseOpenMPReductionId(Parser &P) {
Token Tok = P.getCurToken();
Sema &Actions = P.getActions();
@@ -2614,10 +2630,11 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective(
case OMPD_metadirective: {
ConsumeToken();
SmallVector<VariantMatchInfo, 4> VMIs;
+ SmallVector<OMPTraitInfo *, 4> TraitInfos;
// First iteration of parsing all clauses of metadirective.
- // This iteration only parses and collects all context selector ignoring the
- // associated directives.
+ // This iteration only parses and collects all context selectors ignoring
+ // the associated directives.
TentativeParsingAction TPA(*this);
ASTContext &ASTContext = Actions.getASTContext();
@@ -2693,6 +2710,7 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective(
TI.getAsVariantMatchInfo(ASTContext, VMI);
VMIs.push_back(VMI);
+ TraitInfos.push_back(&TI);
}
TPA.Revert();
@@ -2712,6 +2730,75 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective(
// A single match is returned for OpenMP 5.0
int BestIdx = getBestVariantMatchForContext(VMIs, OMPCtx);
+ // Check if we have user conditions with non-constant expressions that
+ // require runtime selection.
+ bool HasUserCondition = false;
+ for (const VariantMatchInfo &VMI : VMIs) {
+ if (VMI.HasNonConstantUserCondition) {
+ HasUserCondition = true;
+ break;
+ }
+ }
+
+ // If we have user conditions that couldn't be resolved at compile time,
+ // parse all variants and the body.
+ if (HasUserCondition) {
+ SmallVector<OpenMPDirectiveKind, 4> DirectiveKinds;
+ SmallVector<OpenMPClauseKind, 4> ClauseKinds;
+
+ // TODO: Phase 2 - Parse directive clauses and store them.
+ // For now in Phase 1, we only extract directive kinds.
+ // Sema will extract conditions from TraitInfos.
+
+ BalancedDelimiterTracker T(*this, tok::l_paren,
+ tok::annot_pragma_openmp_end);
+ while (Tok.isNot(tok::annot_pragma_openmp_end)) {
+ OpenMPClauseKind CKind =
+ Tok.isAnnotation() ? OMPC_unknown
+ : getOpenMPClauseKind(PP.getSpelling(Tok));
+ SourceLocation ClauseLoc = ConsumeToken();
+
+ // Parse '('.
+ T.consumeOpen();
+
+ if (CKind == OMPC_when) {
+ OMPTraitInfo &TI = Actions.getASTContext().getNewOMPTraitInfo();
+ parseOMPContextSelectors(ClauseLoc, TI);
+
+ // Parse ':'.
+ if (Tok.is(tok::colon))
+ ConsumeAnyToken();
+ }
+
+ // Parse directive kind only for now.
+ OpenMPDirectiveKind DKind = OMPD_unknown;
+ if (!Tok.is(tok::r_paren)) {
+ DKind = parseOpenMPDirectiveKind(*this);
+ skipToMatchingParen(*this);
+ }
+
+ // Parse ')'.
+ if (Tok.is(tok::r_paren))
+ T.consumeClose();
+
+ DirectiveKinds.push_back(DKind);
+ ClauseKinds.push_back(CKind);
+ }
+
+ SourceLocation EndLoc = Tok.getLocation();
+ ConsumeAnnotationToken();
+
+ // Parse the body statement.
+ StmtResult AssociatedStmt = ParseStatement();
+ if (AssociatedStmt.isInvalid())
+ return StmtError();
+
+ // Pass to Sema for Phase 2 processing.
+ return Actions.OpenMP().ActOnOpenMPMetaDirective(
+ Loc, EndLoc, TraitInfos, ClauseKinds, DirectiveKinds,
+ AssociatedStmt.get());
+ }
+
int Idx = 0;
// In OpenMP 5.0 metadirective is either replaced by another directive or
// ignored.
diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index 2e4d9f2f82f0b..90768a0998c8d 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -3771,6 +3771,15 @@ StmtResult SemaOpenMP::ActOnOpenMPAssumeDirective(ArrayRef<OMPClause *> Clauses,
AStmt);
}
+StmtResult SemaOpenMP::ActOnOpenMPMetaDirective(
+ SourceLocation StartLoc, SourceLocation EndLoc,
+ ArrayRef<OMPTraitInfo *> TraitInfos, ArrayRef<OpenMPClauseKind> ClauseKinds,
+ ArrayRef<OpenMPDirectiveKind> DirectiveKinds, Stmt *AssociatedStmt) {
+ // Stub for Phase 1 (Parser) testing.
+ // Sema will extract conditions from TraitInfos in Phase 2.
+ return AssociatedStmt;
+}
+
OMPRequiresDecl *
SemaOpenMP::CheckOMPRequiresDecl(SourceLocation Loc,
ArrayRef<OMPClause *> ClauseList) {
diff --git a/clang/test/OpenMP/metadirective_user_condition_parse.cpp b/clang/test/OpenMP/metadirective_user_condition_parse.cpp
new file mode 100644
index 0000000000000..7306a5ab107fa
--- /dev/null
+++ b/clang/test/OpenMP/metadirective_user_condition_parse.cpp
@@ -0,0 +1,134 @@
+// RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=52 -std=c++11 \
+// RUN: -fsyntax-only %s
+
+// expected-no-diagnostics
+
+void test_runtime_condition(int flag) {
+#pragma omp metadirective \
+ when(user={condition(flag)}: parallel) \
+ otherwise(single)
+ {
+ int x = 0;
+ }
+}
+
+void test_two_conditions(int flag1, int flag2) {
+#pragma omp metadirective \
+ when(user={condition(flag1)}: parallel) \
+ when(user={condition(flag2)}: single) \
+ otherwise()
+ {
+ int y = 1;
+ }
+}
+
+void test_complex_condition(int a, int b) {
+#pragma omp metadirective \
+ when(user={condition(a > b)}: parallel) \
+ otherwise(single)
+ {
+ int z = 2;
+ }
+}
+
+void test_logical_condition(bool flag1, bool flag2) {
+#pragma omp metadirective \
+ when(user={condition(flag1 && flag2)}: parallel) \
+ otherwise()
+ {
+ int w = 3;
+ }
+}
+
+void test_multiple_variants(int flag1, int flag2, int flag3) {
+#pragma omp metadirective \
+ when(user={condition(flag1)}: parallel) \
+ when(user={condition(flag2)}: single) \
+ when(user={condition(flag3)}: teams) \
+ otherwise()
+ {
+ int v = 4;
+ }
+}
+
+void test_otherwise_only() {
+#pragma omp metadirective otherwise(parallel)
+ {
+ int u = 5;
+ }
+}
+
+void test_different_directives(int flag) {
+#pragma omp metadirective \
+ when(user={condition(flag)}: teams) \
+ otherwise(task)
+ {
+ int t = 6;
+ }
+}
+
+void test_nested_statement(int flag) {
+#pragma omp metadirective \
+ when(user={condition(flag)}: parallel) \
+ otherwise()
+ {
+ for (int i = 0; i < 10; ++i) {
+ int s = i;
+ }
+ }
+}
+
+template <int N>
+void test_nontype_template(int flag) {
+#pragma omp metadirective \
+ when(user={condition(N > 0)}: parallel) \
+ otherwise(single)
+ {
+ int x = N;
+ }
+}
+
+template <int Threshold>
+void test_threshold_condition(int value) {
+#pragma omp metadirective \
+ when(user={condition(value > Threshold)}: parallel) \
+ otherwise()
+ {
+ int y = value;
+ }
+}
+
+template <bool UseParallel>
+void test_bool_template() {
+#pragma omp metadirective \
+ when(user={condition(UseParallel)}: parallel) \
+ otherwise(single)
+ {
+ int z = 0;
+ }
+}
+
+template <typename T>
+void test_sizeof_condition(T* ptr) {
+#pragma omp metadirective \
+ when(user={condition(sizeof(T) > 4)}: parallel) \
+ otherwise(single)
+ {
+ T val = *ptr;
+ }
+}
+
+void instantiate_templates() {
+ int flag = 1;
+ int value = 10;
+ int iptr;
+ double dptr;
+
+ test_nontype_template<5>(flag);
+ test_nontype_template<-3>(flag);
+ test_threshold_condition<100>(value);
+ test_bool_template<true>();
+ test_bool_template<false>();
+ test_sizeof_condition<int>(&iptr);
+ test_sizeof_condition<double>(&dptr);
+}
diff --git a/llvm/include/llvm/Frontend/OpenMP/OMPContext.h b/llvm/include/llvm/Frontend/OpenMP/OMPContext.h
index 7849d32665994..7c069721ba677 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMPContext.h
+++ b/llvm/include/llvm/Frontend/OpenMP/OMPContext.h
@@ -156,6 +156,10 @@ struct VariantMatchInfo {
SmallVector<StringRef, 8> ISATraits;
SmallVector<TraitProperty, 8> ConstructTraits;
SmallDenseMap<TraitProperty, APInt> ScoreMap;
+
+ /// True if this variant has a user condition that could not be evaluated at
+ /// compile time (non-constant expression).
+ bool HasNonConstantUserCondition = false;
};
/// The context for a source location is made up of active property traits,
>From 57cccb876e9a98a991ee627593933a15c1da5369 Mon Sep 17 00:00:00 2001
From: Zahira Ammarguellat <zahira.ammarguellat at intel.com>
Date: Tue, 18 Aug 2026 06:08:45 -0700
Subject: [PATCH 2/5] Fix format
---
clang/include/clang/Sema/SemaOpenMP.h | 11 ++++++-----
clang/lib/Parse/ParseOpenMP.cpp | 6 +++---
2 files changed, 9 insertions(+), 8 deletions(-)
diff --git a/clang/include/clang/Sema/SemaOpenMP.h b/clang/include/clang/Sema/SemaOpenMP.h
index 71a43da569de6..1b6faa20b31df 100644
--- a/clang/include/clang/Sema/SemaOpenMP.h
+++ b/clang/include/clang/Sema/SemaOpenMP.h
@@ -219,11 +219,12 @@ class SemaOpenMP : public SemaBase {
/// Called for metadirectives with user conditions that may require runtime
/// selection.
- StmtResult ActOnOpenMPMetaDirective(
- SourceLocation StartLoc, SourceLocation EndLoc,
- ArrayRef<OMPTraitInfo *> TraitInfos,
- ArrayRef<OpenMPClauseKind> ClauseKinds,
- ArrayRef<OpenMPDirectiveKind> DirectiveKinds, Stmt *AssociatedStmt);
+ StmtResult
+ ActOnOpenMPMetaDirective(SourceLocation StartLoc, SourceLocation EndLoc,
+ ArrayRef<OMPTraitInfo *> TraitInfos,
+ ArrayRef<OpenMPClauseKind> ClauseKinds,
+ ArrayRef<OpenMPDirectiveKind> DirectiveKinds,
+ Stmt *AssociatedStmt);
// 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 1f2c48f17e9f1..39d80ca095aa3 100644
--- a/clang/lib/Parse/ParseOpenMP.cpp
+++ b/clang/lib/Parse/ParseOpenMP.cpp
@@ -2753,9 +2753,9 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective(
BalancedDelimiterTracker T(*this, tok::l_paren,
tok::annot_pragma_openmp_end);
while (Tok.isNot(tok::annot_pragma_openmp_end)) {
- OpenMPClauseKind CKind =
- Tok.isAnnotation() ? OMPC_unknown
- : getOpenMPClauseKind(PP.getSpelling(Tok));
+ OpenMPClauseKind CKind = Tok.isAnnotation()
+ ? OMPC_unknown
+ : getOpenMPClauseKind(PP.getSpelling(Tok));
SourceLocation ClauseLoc = ConsumeToken();
// Parse '('.
>From b65daef1bb0f17b8d58ef98f72e91a49fee11267 Mon Sep 17 00:00:00 2001
From: Zahira Ammarguellat <zahira.ammarguellat at intel.com>
Date: Fri, 21 Aug 2026 14:04:49 -0700
Subject: [PATCH 3/5] Phase 2 of metadirective runtime selection
---
clang/include/clang/Lex/Preprocessor.h | 5 +
clang/include/clang/Sema/SemaOpenMP.h | 13 +-
clang/lib/Lex/PPCaching.cpp | 18 ++
clang/lib/Parse/ParseOpenMP.cpp | 187 ++++++++++++++----
clang/lib/Sema/SemaOpenMP.cpp | 28 ++-
.../metadirective_user_condition_parse.cpp | 54 -----
6 files changed, 207 insertions(+), 98 deletions(-)
diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h
index d94f3d2cbe8ed..575bb0af6127c 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -1806,6 +1806,11 @@ class Preprocessor {
/// Disable the last EnableBacktrackAtThisPos call.
void CommitBacktrackedTokens();
+ /// Get the tokens cached since EnableBacktrackAtThisPos() and commit
+ /// the backtrack. The main token stream remains at the current position
+ /// (advanced past the cached tokens).
+ ArrayRef<Token> GetAndCommitBacktrackedTokens();
+
/// Make Preprocessor re-lex the tokens that were lexed since
/// EnableBacktrackAtThisPos() was previously called.
void Backtrack();
diff --git a/clang/include/clang/Sema/SemaOpenMP.h b/clang/include/clang/Sema/SemaOpenMP.h
index 1b6faa20b31df..a4a4a63b6ee79 100644
--- a/clang/include/clang/Sema/SemaOpenMP.h
+++ b/clang/include/clang/Sema/SemaOpenMP.h
@@ -219,12 +219,13 @@ class SemaOpenMP : public SemaBase {
/// Called for metadirectives with user conditions that may require runtime
/// selection.
- StmtResult
- ActOnOpenMPMetaDirective(SourceLocation StartLoc, SourceLocation EndLoc,
- ArrayRef<OMPTraitInfo *> TraitInfos,
- ArrayRef<OpenMPClauseKind> ClauseKinds,
- ArrayRef<OpenMPDirectiveKind> DirectiveKinds,
- Stmt *AssociatedStmt);
+ StmtResult ActOnOpenMPMetaDirective(
+ SourceLocation StartLoc, SourceLocation EndLoc,
+ ArrayRef<OMPTraitInfo *> TraitInfos,
+ ArrayRef<OpenMPClauseKind> ClauseKinds,
+ ArrayRef<OpenMPDirectiveKind> DirectiveKinds,
+ ArrayRef<ArrayRef<OMPClause *>> DirectiveClauses,
+ ArrayRef<Stmt *> VariantBodies);
// OpenMP directives and clauses.
/// Called on correct id-expression from the '#pragma omp
diff --git a/clang/lib/Lex/PPCaching.cpp b/clang/lib/Lex/PPCaching.cpp
index 3f0ebd8455685..b21d97dd77fa0 100644
--- a/clang/lib/Lex/PPCaching.cpp
+++ b/clang/lib/Lex/PPCaching.cpp
@@ -61,6 +61,24 @@ void Preprocessor::CommitBacktrackedTokens() {
PopUnannotatedBacktrackTokens();
}
+// Get the tokens cached since EnableBacktrackAtThisPos() and commit
+// the backtrack.
+ArrayRef<Token> Preprocessor::GetAndCommitBacktrackedTokens() {
+ assert(isBacktrackEnabled() &&
+ "Should only be called when backtracking is enabled");
+
+ auto [LastPos, Unannotated] = LastBacktrackPos();
+
+ // Get range of tokens cached since EnableBacktrackAtThisPos.
+ ArrayRef<Token> CachedRange(CachedTokens.begin() + LastPos,
+ CachedTokens.begin() + CachedLexPos);
+
+ // Commit backtrack to keep stream advanced.
+ CommitBacktrackedTokens();
+
+ return CachedRange;
+}
+
// Make Preprocessor re-lex the tokens that were lexed since
// EnableBacktrackAtThisPos() was previously called.
void Preprocessor::Backtrack() {
diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp
index 39d80ca095aa3..fc3ee0b0acbde 100644
--- a/clang/lib/Parse/ParseOpenMP.cpp
+++ b/clang/lib/Parse/ParseOpenMP.cpp
@@ -108,22 +108,6 @@ static OpenMPDirectiveKind parseOpenMPDirectiveKind(Parser &P) {
return checkOpenMPDirectiveName(P, Loc, S->Value, Concat);
}
-/// Skip tokens until reaching the matching closing parenthesis.
-/// Handles nested parentheses correctly.
-static void skipToMatchingParen(Parser &P) {
- int ParenDepth = 0;
- while ((P.getCurToken().isNot(tok::r_paren) || ParenDepth != 0) &&
- P.getCurToken().isNot(tok::annot_pragma_openmp_end) &&
- P.getCurToken().isNot(tok::eof)) {
- if (P.getCurToken().is(tok::l_paren))
- ParenDepth++;
- if (P.getCurToken().is(tok::r_paren) && ParenDepth > 0)
- ParenDepth--;
- if (ParenDepth > 0 || P.getCurToken().isNot(tok::r_paren))
- P.ConsumeAnyToken();
- }
-}
-
static DeclarationName parseOpenMPReductionId(Parser &P) {
Token Tok = P.getCurToken();
Sema &Actions = P.getActions();
@@ -2740,22 +2724,20 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective(
}
}
- // If we have user conditions that couldn't be resolved at compile time,
- // parse all variants and the body.
+ // Different directives have different data-sharing attributes, so each
+ // variant needs its own CapturedStmt with proper DSA context.
+ // We manually cache body tokens and inject them for each variant parse.
if (HasUserCondition) {
- SmallVector<OpenMPDirectiveKind, 4> DirectiveKinds;
SmallVector<OpenMPClauseKind, 4> ClauseKinds;
-
- // TODO: Phase 2 - Parse directive clauses and store them.
- // For now in Phase 1, we only extract directive kinds.
- // Sema will extract conditions from TraitInfos.
+ SmallVector<OpenMPDirectiveKind, 4> DirectiveKinds;
+ SmallVector<SmallVector<OMPClause *, 5>, 4> DirectiveClauses;
BalancedDelimiterTracker T(*this, tok::l_paren,
tok::annot_pragma_openmp_end);
while (Tok.isNot(tok::annot_pragma_openmp_end)) {
- OpenMPClauseKind CKind = Tok.isAnnotation()
- ? OMPC_unknown
- : getOpenMPClauseKind(PP.getSpelling(Tok));
+ OpenMPClauseKind CKind =
+ Tok.isAnnotation() ? OMPC_unknown
+ : getOpenMPClauseKind(PP.getSpelling(Tok));
SourceLocation ClauseLoc = ConsumeToken();
// Parse '('.
@@ -2770,33 +2752,170 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective(
ConsumeAnyToken();
}
- // Parse directive kind only for now.
+ // Parse the directive kind and clauses manually.
OpenMPDirectiveKind DKind = OMPD_unknown;
+ SmallVector<OMPClause *, 5> Clauses;
+
if (!Tok.is(tok::r_paren)) {
+ // Parse directive kind (handles combined directives like
+ // 'parallel for simd').
DKind = parseOpenMPDirectiveKind(*this);
- skipToMatchingParen(*this);
+
+ // Consume the last token of the directive name.
+ if (Tok.isNot(tok::annot_pragma_openmp_end) &&
+ Tok.isNot(tok::r_paren))
+ ConsumeAnyToken();
+
+ // Parse clauses for this directive if any exist.
+ // We stop at ')' which ends the metadirective variant.
+ while (Tok.isNot(tok::r_paren) &&
+ Tok.isNot(tok::annot_pragma_openmp_end)) {
+ // Check if current token is a clause keyword.
+ OpenMPClauseKind ClauseKind =
+ Tok.isAnnotation()
+ ? OMPC_unknown
+ : getOpenMPClauseKind(PP.getSpelling(Tok));
+
+ // If not a clause keyword, we've parsed all clauses.
+ if (ClauseKind == OMPC_unknown)
+ break;
+
+ Actions.OpenMP().StartOpenMPClause(ClauseKind);
+ OMPClause *Clause =
+ ParseOpenMPClause(DKind, ClauseKind,
+ /* FirstClause */ Clauses.empty());
+ Actions.OpenMP().EndOpenMPClause();
+
+ if (Clause)
+ Clauses.push_back(Clause);
+
+ // Check for comma separator between clauses.
+ if (Tok.is(tok::comma))
+ ConsumeAnyToken();
+ else if (Tok.isNot(tok::r_paren) &&
+ Tok.isNot(tok::annot_pragma_openmp_end)) {
+ // Unexpected token - not comma, not closing paren.
+ break;
+ }
+ }
}
// Parse ')'.
if (Tok.is(tok::r_paren))
T.consumeClose();
- DirectiveKinds.push_back(DKind);
ClauseKinds.push_back(CKind);
+ DirectiveKinds.push_back(DKind);
+ DirectiveClauses.push_back(Clauses);
}
SourceLocation EndLoc = Tok.getLocation();
ConsumeAnnotationToken();
- // Parse the body statement.
- StmtResult AssociatedStmt = ParseStatement();
- if (AssociatedStmt.isInvalid())
+ // Manually cache all body tokens for unlimited replay.
+ SmallVector<Token, 64> BodyTokens;
+
+ // Cache the current token before enabling backtracking.
+ BodyTokens.push_back(Tok);
+
+ // Parse the statement once and cache remaining tokens.
+ PP.EnableBacktrackAtThisPos();
+ {
+ ParsingOpenMPDirectiveRAII NormalScope(*this, /*Value=*/false);
+ StmtResult BodyStmt = ParseStatement();
+ if (BodyStmt.isInvalid()) {
+ PP.Backtrack();
+ return StmtError();
+ }
+ }
+
+ // Get cached tokens and commit (keeps stream advanced).
+ ArrayRef<Token> CachedRange = PP.GetAndCommitBacktrackedTokens();
+ BodyTokens.append(CachedRange.begin(), CachedRange.end());
+
+ if (BodyTokens.empty()) {
+ Diag(Tok, diag::err_expected_statement);
return StmtError();
+ }
+
+ // Add an EOF token with marker to end the injected stream.
+ Token EofToken;
+ EofToken.startToken();
+ EofToken.setKind(tok::eof);
+ EofToken.setLocation(Tok.getLocation());
+ EofToken.setEofData(this);
+ BodyTokens.push_back(EofToken);
+
+ // Parse the body separately for each variant to get correct DSA.
+ SmallVector<Stmt *, 4> VariantBodies;
+ for (unsigned i = 0; i < DirectiveKinds.size(); ++i) {
+ if (DirectiveKinds[i] == OMPD_unknown) {
+ VariantBodies.push_back(nullptr);
+ continue;
+ }
+
+ auto TokensCopy = std::make_unique<Token[]>(BodyTokens.size());
+ std::copy(BodyTokens.begin(), BodyTokens.end(), TokensCopy.get());
+ PP.EnterTokenStream(std::move(TokensCopy), BodyTokens.size(),
+ /*DisableMacroExpansion=*/false,
+ /*IsReinject=*/false);
+ // Consume first token from the injected stream to update Tok.
+ ConsumeAnyToken();
+
+ // Start DSA block for this directive.
+ Actions.OpenMP().StartOpenMPDSABlock(DirectiveKinds[i],
+ DeclarationNameInfo(),
+ getCurScope(), Loc);
+
+ // Start captured region for this directive.
+ Actions.OpenMP().ActOnOpenMPRegionStart(DirectiveKinds[i],
+ getCurScope());
+
+ // Parse the body.
+ StmtResult Body;
+ ParsingOpenMPDirectiveRAII NormalScope(*this, /*Value=*/false);
+ {
+ Sema::CompoundScopeRAII Scope(Actions);
+ Body = ParseStatement();
+ }
+
+ if (Body.isInvalid())
+ return StmtError();
+
+ // End captured region - wraps body in CapturedStmt.
+ Body = Actions.OpenMP().ActOnOpenMPRegionEnd(Body,
+ DirectiveClauses[i]);
+ if (Body.isInvalid())
+ return StmtError();
+
+ // End DSA block for this variant.
+ Actions.OpenMP().EndOpenMPDSABlock(Body.get());
+
+ VariantBodies.push_back(Body.get());
+
+ // Clean up this variant's injected token stream.
+ // Skip cleanup for the last variant to avoid stream corruption.
+ if (i < DirectiveKinds.size() - 1) {
+ while (Tok.isNot(tok::eof))
+ ConsumeAnyToken();
+
+ // Consume the marked EOF to pop the injected stream.
+ if (Tok.is(tok::eof) && Tok.getEofData() == this)
+ ConsumeAnyToken();
+ }
+ }
+
+ // The last variant's token stream remains active. The variants'
+ // CapturedStmts already include the body.
+
+ // Convert DirectiveClauses to ArrayRef<ArrayRef<OMPClause *>>.
+ SmallVector<ArrayRef<OMPClause *>, 4> ClausesArrayRefs;
+ for (const auto &Clauses : DirectiveClauses)
+ ClausesArrayRefs.push_back(Clauses);
- // Pass to Sema for Phase 2 processing.
return Actions.OpenMP().ActOnOpenMPMetaDirective(
Loc, EndLoc, TraitInfos, ClauseKinds, DirectiveKinds,
- AssociatedStmt.get());
+ ClausesArrayRefs, VariantBodies);
}
int Idx = 0;
diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index 90768a0998c8d..14a6988cbd53a 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -3774,10 +3774,30 @@ StmtResult SemaOpenMP::ActOnOpenMPAssumeDirective(ArrayRef<OMPClause *> Clauses,
StmtResult SemaOpenMP::ActOnOpenMPMetaDirective(
SourceLocation StartLoc, SourceLocation EndLoc,
ArrayRef<OMPTraitInfo *> TraitInfos, ArrayRef<OpenMPClauseKind> ClauseKinds,
- ArrayRef<OpenMPDirectiveKind> DirectiveKinds, Stmt *AssociatedStmt) {
- // Stub for Phase 1 (Parser) testing.
- // Sema will extract conditions from TraitInfos in Phase 2.
- return AssociatedStmt;
+ ArrayRef<OpenMPDirectiveKind> DirectiveKinds,
+ ArrayRef<ArrayRef<OMPClause *>> DirectiveClauses,
+ ArrayRef<Stmt *> VariantBodies) {
+
+ assert((ClauseKinds.size() == DirectiveKinds.size() &&
+ DirectiveKinds.size() == DirectiveClauses.size() &&
+ DirectiveClauses.size() == VariantBodies.size()) &&
+ "Mismatched variant arrays");
+
+ // VariantBodies are complete CapturedStmt nodes with DSA from Parser.
+ // TODO Phase 3: Build OMPMetaDirective AST node holding all variants.
+ // TODO Phase 3: Extract conditions from TraitInfos.
+ // TODO Phase 4: Codegen - generate runtime if-else chain.
+
+ // Temporary stub: return first variant until OMPMetaDirective exists.
+ // Early return if no variants were successfully parsed.
+ if (VariantBodies.empty())
+ return StmtError();
+
+ // Return first variant if it's valid.
+ if (VariantBodies[0])
+ return VariantBodies[0];
+
+ return StmtError();
}
OMPRequiresDecl *
diff --git a/clang/test/OpenMP/metadirective_user_condition_parse.cpp b/clang/test/OpenMP/metadirective_user_condition_parse.cpp
index 7306a5ab107fa..6d121c42302e4 100644
--- a/clang/test/OpenMP/metadirective_user_condition_parse.cpp
+++ b/clang/test/OpenMP/metadirective_user_condition_parse.cpp
@@ -78,57 +78,3 @@ void test_nested_statement(int flag) {
}
}
-template <int N>
-void test_nontype_template(int flag) {
-#pragma omp metadirective \
- when(user={condition(N > 0)}: parallel) \
- otherwise(single)
- {
- int x = N;
- }
-}
-
-template <int Threshold>
-void test_threshold_condition(int value) {
-#pragma omp metadirective \
- when(user={condition(value > Threshold)}: parallel) \
- otherwise()
- {
- int y = value;
- }
-}
-
-template <bool UseParallel>
-void test_bool_template() {
-#pragma omp metadirective \
- when(user={condition(UseParallel)}: parallel) \
- otherwise(single)
- {
- int z = 0;
- }
-}
-
-template <typename T>
-void test_sizeof_condition(T* ptr) {
-#pragma omp metadirective \
- when(user={condition(sizeof(T) > 4)}: parallel) \
- otherwise(single)
- {
- T val = *ptr;
- }
-}
-
-void instantiate_templates() {
- int flag = 1;
- int value = 10;
- int iptr;
- double dptr;
-
- test_nontype_template<5>(flag);
- test_nontype_template<-3>(flag);
- test_threshold_condition<100>(value);
- test_bool_template<true>();
- test_bool_template<false>();
- test_sizeof_condition<int>(&iptr);
- test_sizeof_condition<double>(&dptr);
-}
>From cba2ebd8323377cfee1ade555b74b40396a9aa4f Mon Sep 17 00:00:00 2001
From: Zahira Ammarguellat <zahira.ammarguellat at intel.com>
Date: Mon, 31 Aug 2026 12:32:09 -0700
Subject: [PATCH 4/5] Fixed format, addressed part of the review comments
---
clang/include/clang/AST/OpenMPClause.h | 16 +++----
clang/include/clang/Sema/SemaOpenMP.h | 14 +++---
clang/lib/Lex/PPCaching.cpp | 2 +-
clang/lib/Parse/ParseOpenMP.cpp | 60 +++++++++++++-------------
4 files changed, 46 insertions(+), 46 deletions(-)
diff --git a/clang/include/clang/AST/OpenMPClause.h b/clang/include/clang/AST/OpenMPClause.h
index c39af8d3148f8..0bb46f8d1da0c 100644
--- a/clang/include/clang/AST/OpenMPClause.h
+++ b/clang/include/clang/AST/OpenMPClause.h
@@ -10131,15 +10131,13 @@ class OMPTraitInfo {
/// Check if this trait info contains any user conditions.
bool hasUserCondition() const {
- for (const OMPTraitSet &Set : Sets) {
- if (Set.Kind != llvm::omp::TraitSet::user)
- continue;
- for (const OMPTraitSelector &Selector : Set.Selectors) {
- if (Selector.Kind == llvm::omp::TraitSelector::user_condition)
- return true;
- }
- }
- return false;
+ return llvm::any_of(Sets, [](const OMPTraitSet &Set) {
+ return Set.Kind == llvm::omp::TraitSet::user &&
+ llvm::any_of(Set.Selectors, [](const OMPTraitSelector &Selector) {
+ return Selector.Kind ==
+ llvm::omp::TraitSelector::user_condition;
+ });
+ });
}
/// Print a human readable representation into \p OS.
diff --git a/clang/include/clang/Sema/SemaOpenMP.h b/clang/include/clang/Sema/SemaOpenMP.h
index a4a4a63b6ee79..ba4c9e8af8b9a 100644
--- a/clang/include/clang/Sema/SemaOpenMP.h
+++ b/clang/include/clang/Sema/SemaOpenMP.h
@@ -219,13 +219,13 @@ class SemaOpenMP : public SemaBase {
/// Called for metadirectives with user conditions that may require runtime
/// selection.
- StmtResult ActOnOpenMPMetaDirective(
- SourceLocation StartLoc, SourceLocation EndLoc,
- ArrayRef<OMPTraitInfo *> TraitInfos,
- ArrayRef<OpenMPClauseKind> ClauseKinds,
- ArrayRef<OpenMPDirectiveKind> DirectiveKinds,
- ArrayRef<ArrayRef<OMPClause *>> DirectiveClauses,
- ArrayRef<Stmt *> VariantBodies);
+ StmtResult
+ ActOnOpenMPMetaDirective(SourceLocation StartLoc, SourceLocation EndLoc,
+ ArrayRef<OMPTraitInfo *> TraitInfos,
+ ArrayRef<OpenMPClauseKind> ClauseKinds,
+ ArrayRef<OpenMPDirectiveKind> DirectiveKinds,
+ ArrayRef<ArrayRef<OMPClause *>> DirectiveClauses,
+ ArrayRef<Stmt *> VariantBodies);
// OpenMP directives and clauses.
/// Called on correct id-expression from the '#pragma omp
diff --git a/clang/lib/Lex/PPCaching.cpp b/clang/lib/Lex/PPCaching.cpp
index b21d97dd77fa0..be1e8d88630bb 100644
--- a/clang/lib/Lex/PPCaching.cpp
+++ b/clang/lib/Lex/PPCaching.cpp
@@ -71,7 +71,7 @@ ArrayRef<Token> Preprocessor::GetAndCommitBacktrackedTokens() {
// Get range of tokens cached since EnableBacktrackAtThisPos.
ArrayRef<Token> CachedRange(CachedTokens.begin() + LastPos,
- CachedTokens.begin() + CachedLexPos);
+ CachedTokens.begin() + CachedLexPos);
// Commit backtrack to keep stream advanced.
CommitBacktrackedTokens();
diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp
index fc3ee0b0acbde..47d1ada0ca0da 100644
--- a/clang/lib/Parse/ParseOpenMP.cpp
+++ b/clang/lib/Parse/ParseOpenMP.cpp
@@ -2716,13 +2716,9 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective(
// Check if we have user conditions with non-constant expressions that
// require runtime selection.
- bool HasUserCondition = false;
- for (const VariantMatchInfo &VMI : VMIs) {
- if (VMI.HasNonConstantUserCondition) {
- HasUserCondition = true;
- break;
- }
- }
+ bool HasUserCondition = llvm::any_of(VMIs, [](const VariantMatchInfo &VMI) {
+ return VMI.HasNonConstantUserCondition;
+ });
// Different directives have different data-sharing attributes, so each
// variant needs its own CapturedStmt with proper DSA context.
@@ -2735,9 +2731,9 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective(
BalancedDelimiterTracker T(*this, tok::l_paren,
tok::annot_pragma_openmp_end);
while (Tok.isNot(tok::annot_pragma_openmp_end)) {
- OpenMPClauseKind CKind =
- Tok.isAnnotation() ? OMPC_unknown
- : getOpenMPClauseKind(PP.getSpelling(Tok));
+ OpenMPClauseKind CKind = Tok.isAnnotation()
+ ? OMPC_unknown
+ : getOpenMPClauseKind(PP.getSpelling(Tok));
SourceLocation ClauseLoc = ConsumeToken();
// Parse '('.
@@ -2772,9 +2768,8 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective(
Tok.isNot(tok::annot_pragma_openmp_end)) {
// Check if current token is a clause keyword.
OpenMPClauseKind ClauseKind =
- Tok.isAnnotation()
- ? OMPC_unknown
- : getOpenMPClauseKind(PP.getSpelling(Tok));
+ Tok.isAnnotation() ? OMPC_unknown
+ : getOpenMPClauseKind(PP.getSpelling(Tok));
// If not a clause keyword, we've parsed all clauses.
if (ClauseKind == OMPC_unknown)
@@ -2818,11 +2813,23 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective(
// Cache the current token before enabling backtracking.
BodyTokens.push_back(Tok);
- // Parse the statement once and cache remaining tokens.
+ // Parse the statement once to cache tokens.
+ // Suppress diagnostics since this is only for token boundary detection.
+ // Actual parsing with DSA happens in the variant loop.
PP.EnableBacktrackAtThisPos();
{
ParsingOpenMPDirectiveRAII NormalScope(*this, /*Value=*/false);
+
+ // Suppress diagnostics during token caching parse.
+ DiagnosticsEngine &Diags = PP.getDiagnostics();
+ bool OldSuppressAllDiagnostics = Diags.getSuppressAllDiagnostics();
+ Diags.setSuppressAllDiagnostics(true);
+
StmtResult BodyStmt = ParseStatement();
+
+ // Restore diagnostic state.
+ Diags.setSuppressAllDiagnostics(OldSuppressAllDiagnostics);
+
if (BodyStmt.isInvalid()) {
PP.Backtrack();
return StmtError();
@@ -2833,10 +2840,7 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective(
ArrayRef<Token> CachedRange = PP.GetAndCommitBacktrackedTokens();
BodyTokens.append(CachedRange.begin(), CachedRange.end());
- if (BodyTokens.empty()) {
- Diag(Tok, diag::err_expected_statement);
- return StmtError();
- }
+ assert(!BodyTokens.empty() && "Body tokens should not be empty");
// Add an EOF token with marker to end the injected stream.
Token EofToken;
@@ -2848,8 +2852,8 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective(
// Parse the body separately for each variant to get correct DSA.
SmallVector<Stmt *, 4> VariantBodies;
- for (unsigned i = 0; i < DirectiveKinds.size(); ++i) {
- if (DirectiveKinds[i] == OMPD_unknown) {
+ for (unsigned I : llvm::seq<unsigned>(DirectiveKinds.size())) {
+ if (DirectiveKinds[I] == OMPD_unknown) {
VariantBodies.push_back(nullptr);
continue;
}
@@ -2857,18 +2861,17 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective(
auto TokensCopy = std::make_unique<Token[]>(BodyTokens.size());
std::copy(BodyTokens.begin(), BodyTokens.end(), TokensCopy.get());
PP.EnterTokenStream(std::move(TokensCopy), BodyTokens.size(),
- /*DisableMacroExpansion=*/false,
- /*IsReinject=*/false);
+ /*DisableMacroExpansion=*/false,
+ /*IsReinject=*/false);
// Consume first token from the injected stream to update Tok.
ConsumeAnyToken();
// Start DSA block for this directive.
- Actions.OpenMP().StartOpenMPDSABlock(DirectiveKinds[i],
- DeclarationNameInfo(),
- getCurScope(), Loc);
+ Actions.OpenMP().StartOpenMPDSABlock(
+ DirectiveKinds[I], DeclarationNameInfo(), getCurScope(), Loc);
// Start captured region for this directive.
- Actions.OpenMP().ActOnOpenMPRegionStart(DirectiveKinds[i],
+ Actions.OpenMP().ActOnOpenMPRegionStart(DirectiveKinds[I],
getCurScope());
// Parse the body.
@@ -2883,8 +2886,7 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective(
return StmtError();
// End captured region - wraps body in CapturedStmt.
- Body = Actions.OpenMP().ActOnOpenMPRegionEnd(Body,
- DirectiveClauses[i]);
+ Body = Actions.OpenMP().ActOnOpenMPRegionEnd(Body, DirectiveClauses[I]);
if (Body.isInvalid())
return StmtError();
@@ -2895,7 +2897,7 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective(
// Clean up this variant's injected token stream.
// Skip cleanup for the last variant to avoid stream corruption.
- if (i < DirectiveKinds.size() - 1) {
+ if (I < DirectiveKinds.size() - 1) {
while (Tok.isNot(tok::eof))
ConsumeAnyToken();
>From ee610a43c936e65a08e2a587b5b42943cf7ad98e Mon Sep 17 00:00:00 2001
From: Zahira Ammarguellat <zahira.ammarguellat at intel.com>
Date: Thu, 3 Sep 2026 08:23:05 -0700
Subject: [PATCH 5/5] Addressed review comments
---
clang/include/clang/AST/OpenMPClause.h | 3 +-
clang/lib/Parse/ParseOpenMP.cpp | 157 ++++++------------
.../OpenMP/metadirective_variant_clauses.cpp | 42 +++++
3 files changed, 92 insertions(+), 110 deletions(-)
create mode 100644 clang/test/OpenMP/metadirective_variant_clauses.cpp
diff --git a/clang/include/clang/AST/OpenMPClause.h b/clang/include/clang/AST/OpenMPClause.h
index 0bb46f8d1da0c..087b961abf58b 100644
--- a/clang/include/clang/AST/OpenMPClause.h
+++ b/clang/include/clang/AST/OpenMPClause.h
@@ -10134,8 +10134,7 @@ class OMPTraitInfo {
return llvm::any_of(Sets, [](const OMPTraitSet &Set) {
return Set.Kind == llvm::omp::TraitSet::user &&
llvm::any_of(Set.Selectors, [](const OMPTraitSelector &Selector) {
- return Selector.Kind ==
- llvm::omp::TraitSelector::user_condition;
+ return Selector.Kind == llvm::omp::TraitSelector::user_condition;
});
});
}
diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp
index 47d1ada0ca0da..335edeb17804f 100644
--- a/clang/lib/Parse/ParseOpenMP.cpp
+++ b/clang/lib/Parse/ParseOpenMP.cpp
@@ -2748,30 +2748,33 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective(
ConsumeAnyToken();
}
- // Parse the directive kind and clauses manually.
OpenMPDirectiveKind DKind = OMPD_unknown;
SmallVector<OMPClause *, 5> Clauses;
if (!Tok.is(tok::r_paren)) {
- // Parse directive kind (handles combined directives like
- // 'parallel for simd').
+ // Parse directive kind (e.g., 'parallel', 'single').
DKind = parseOpenMPDirectiveKind(*this);
- // Consume the last token of the directive name.
if (Tok.isNot(tok::annot_pragma_openmp_end) &&
Tok.isNot(tok::r_paren))
ConsumeAnyToken();
- // Parse clauses for this directive if any exist.
- // We stop at ')' which ends the metadirective variant.
+ // Open temporary DSA block so clauses have an active directive frame.
+ if (DKind != OMPD_unknown)
+ Actions.OpenMP().StartOpenMPDSABlock(DKind, DeclarationNameInfo(),
+ getCurScope(), ClauseLoc);
+
+ // Parse clauses for this directive variant.
while (Tok.isNot(tok::r_paren) &&
Tok.isNot(tok::annot_pragma_openmp_end)) {
- // Check if current token is a clause keyword.
+ // Skip optional commas between clauses.
+ if (Tok.is(tok::comma))
+ ConsumeAnyToken();
+
OpenMPClauseKind ClauseKind =
Tok.isAnnotation() ? OMPC_unknown
: getOpenMPClauseKind(PP.getSpelling(Tok));
- // If not a clause keyword, we've parsed all clauses.
if (ClauseKind == OMPC_unknown)
break;
@@ -2783,21 +2786,16 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective(
if (Clause)
Clauses.push_back(Clause);
-
- // Check for comma separator between clauses.
- if (Tok.is(tok::comma))
- ConsumeAnyToken();
- else if (Tok.isNot(tok::r_paren) &&
- Tok.isNot(tok::annot_pragma_openmp_end)) {
- // Unexpected token - not comma, not closing paren.
- break;
- }
}
+
+ // Close the temporary DSA block for header parsing.
+ if (DKind != OMPD_unknown)
+ Actions.OpenMP().EndOpenMPDSABlock(nullptr);
}
- // Parse ')'.
- if (Tok.is(tok::r_paren))
- T.consumeClose();
+ // Parse ')' or recover to pragma end on syntax error.
+ if (T.consumeClose())
+ T.skipToEnd();
ClauseKinds.push_back(CKind);
DirectiveKinds.push_back(DKind);
@@ -2807,114 +2805,57 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective(
SourceLocation EndLoc = Tok.getLocation();
ConsumeAnnotationToken();
- // Manually cache all body tokens for unlimited replay.
- SmallVector<Token, 64> BodyTokens;
-
- // Cache the current token before enabling backtracking.
- BodyTokens.push_back(Tok);
-
- // Parse the statement once to cache tokens.
- // Suppress diagnostics since this is only for token boundary detection.
- // Actual parsing with DSA happens in the variant loop.
- PP.EnableBacktrackAtThisPos();
- {
- ParsingOpenMPDirectiveRAII NormalScope(*this, /*Value=*/false);
-
- // Suppress diagnostics during token caching parse.
- DiagnosticsEngine &Diags = PP.getDiagnostics();
- bool OldSuppressAllDiagnostics = Diags.getSuppressAllDiagnostics();
- Diags.setSuppressAllDiagnostics(true);
-
- StmtResult BodyStmt = ParseStatement();
-
- // Restore diagnostic state.
- Diags.setSuppressAllDiagnostics(OldSuppressAllDiagnostics);
-
- if (BodyStmt.isInvalid()) {
- PP.Backtrack();
- return StmtError();
- }
- }
-
- // Get cached tokens and commit (keeps stream advanced).
- ArrayRef<Token> CachedRange = PP.GetAndCommitBacktrackedTokens();
- BodyTokens.append(CachedRange.begin(), CachedRange.end());
-
- assert(!BodyTokens.empty() && "Body tokens should not be empty");
-
- // Add an EOF token with marker to end the injected stream.
- Token EofToken;
- EofToken.startToken();
- EofToken.setKind(tok::eof);
- EofToken.setLocation(Tok.getLocation());
- EofToken.setEofData(this);
- BodyTokens.push_back(EofToken);
-
- // Parse the body separately for each variant to get correct DSA.
+ // Parse the body separately for each variant to establish proper DSA.
SmallVector<Stmt *, 4> VariantBodies;
- for (unsigned I : llvm::seq<unsigned>(DirectiveKinds.size())) {
- if (DirectiveKinds[I] == OMPD_unknown) {
- VariantBodies.push_back(nullptr);
- continue;
- }
- auto TokensCopy = std::make_unique<Token[]>(BodyTokens.size());
- std::copy(BodyTokens.begin(), BodyTokens.end(), TokensCopy.get());
- PP.EnterTokenStream(std::move(TokensCopy), BodyTokens.size(),
- /*DisableMacroExpansion=*/false,
- /*IsReinject=*/false);
- // Consume first token from the injected stream to update Tok.
- ConsumeAnyToken();
+ for (unsigned I : llvm::seq<unsigned>(DirectiveKinds.size())) {
+ std::optional<TentativeParsingAction> TPA;
+ if (I < DirectiveKinds.size() - 1)
+ TPA.emplace(*this);
- // Start DSA block for this directive.
- Actions.OpenMP().StartOpenMPDSABlock(
- DirectiveKinds[I], DeclarationNameInfo(), getCurScope(), Loc);
+ const bool HasDirective = (DirectiveKinds[I] != OMPD_unknown);
- // Start captured region for this directive.
- Actions.OpenMP().ActOnOpenMPRegionStart(DirectiveKinds[I],
- getCurScope());
+ if (HasDirective) {
+ Actions.OpenMP().StartOpenMPDSABlock(
+ DirectiveKinds[I], DeclarationNameInfo(), getCurScope(), Loc);
+ Actions.OpenMP().ActOnOpenMPRegionStart(DirectiveKinds[I],
+ getCurScope());
+ }
- // Parse the body.
StmtResult Body;
ParsingOpenMPDirectiveRAII NormalScope(*this, /*Value=*/false);
{
Sema::CompoundScopeRAII Scope(Actions);
Body = ParseStatement();
}
-
- if (Body.isInvalid())
- return StmtError();
-
- // End captured region - wraps body in CapturedStmt.
- Body = Actions.OpenMP().ActOnOpenMPRegionEnd(Body, DirectiveClauses[I]);
- if (Body.isInvalid())
+ if (Body.isInvalid()) {
+ if (HasDirective)
+ Actions.OpenMP().EndOpenMPDSABlock(nullptr);
+ if (TPA)
+ TPA->Revert();
return StmtError();
+ }
+ if (HasDirective) {
+ Body =
+ Actions.OpenMP().ActOnOpenMPRegionEnd(Body, DirectiveClauses[I]);
+ if (Body.isInvalid()) {
+ Actions.OpenMP().EndOpenMPDSABlock(nullptr);
+ if (TPA)
+ TPA->Revert();
+ return StmtError();
+ }
- // End DSA block for this variant.
- Actions.OpenMP().EndOpenMPDSABlock(Body.get());
-
- VariantBodies.push_back(Body.get());
-
- // Clean up this variant's injected token stream.
- // Skip cleanup for the last variant to avoid stream corruption.
- if (I < DirectiveKinds.size() - 1) {
- while (Tok.isNot(tok::eof))
- ConsumeAnyToken();
-
- // Consume the marked EOF to pop the injected stream.
- if (Tok.is(tok::eof) && Tok.getEofData() == this)
- ConsumeAnyToken();
+ Actions.OpenMP().EndOpenMPDSABlock(Body.get());
}
+ VariantBodies.push_back(Body.get());
+ if (TPA)
+ TPA->Revert();
}
- // The last variant's token stream remains active. The variants'
- // CapturedStmts already include the body.
-
// Convert DirectiveClauses to ArrayRef<ArrayRef<OMPClause *>>.
SmallVector<ArrayRef<OMPClause *>, 4> ClausesArrayRefs;
for (const auto &Clauses : DirectiveClauses)
ClausesArrayRefs.push_back(Clauses);
-
return Actions.OpenMP().ActOnOpenMPMetaDirective(
Loc, EndLoc, TraitInfos, ClauseKinds, DirectiveKinds,
ClausesArrayRefs, VariantBodies);
diff --git a/clang/test/OpenMP/metadirective_variant_clauses.cpp b/clang/test/OpenMP/metadirective_variant_clauses.cpp
new file mode 100644
index 0000000000000..a379a16c17ba8
--- /dev/null
+++ b/clang/test/OpenMP/metadirective_variant_clauses.cpp
@@ -0,0 +1,42 @@
+// RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=52 -std=c++11 \
+// RUN: -fsyntax-only %s
+
+// expected-no-diagnostics
+
+void test_variant_clauses_function_scope(int flag) {
+ int x = 0;
+ int y = 0;
+
+#pragma omp metadirective \
+ when(user={condition(flag)}: parallel private(x)) \
+ when(user={condition(!flag)}: parallel shared(y))
+ {
+ x = 1;
+ y = 2;
+ }
+}
+
+void test_nested_metadirective(int flag1, int flag2) {
+ int x = 0;
+
+#pragma omp parallel private(x)
+ {
+#pragma omp metadirective \
+ when(user={condition(flag1)}: for) \
+ when(user={condition(flag2)}: single private(x))
+ for (int i = 0; i < 10; ++i) {
+ x = i;
+ }
+ }
+}
+
+void test_multiple_clauses(int flag) {
+ int x = 0, y = 0, z = 0;
+
+#pragma omp metadirective \
+ when(user={condition(flag)}: parallel private(x) shared(y) firstprivate(z)) \
+ otherwise(single private(x))
+ {
+ x = y + z;
+ }
+}
More information about the cfe-commits
mailing list