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
