https://github.com/zahiraam updated https://github.com/llvm/llvm-project/pull/192455
>From ecbf23841052ce84e3cc67e0350924f9d63b3f43 Mon Sep 17 00:00:00 2001 From: Zahira Ammarguellat <[email protected]> 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 3b4b9df767e73..9c637e3bd17e0 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 0d5d7b6eb412d4562dd8f08f384355ace1a39512 Mon Sep 17 00:00:00 2001 From: Zahira Ammarguellat <[email protected]> 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 c55259b43af9f67c27eea187d80c0cc87403d17b Mon Sep 17 00:00:00 2001 From: Zahira Ammarguellat <[email protected]> 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 9c637e3bd17e0..6238893325505 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 b695096b64765caf43fad47b5f3bb02d14039019 Mon Sep 17 00:00:00 2001 From: Zahira Ammarguellat <[email protected]> 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 0f5343a517142f6a50c94b29c1d1dbe8b482a5e0 Mon Sep 17 00:00:00 2001 From: Zahira Ammarguellat <[email protected]> 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; + } +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
