[clang] [llvm] [clang][OpenMP] Issue a warning when parsing future directive spelling (PR #146933)
Krzysztof Parzyszek via llvm-commits
llvm-commits at lists.llvm.org
Mon Jul 7 05:57:09 PDT 2025
https://github.com/kparzysz updated https://github.com/llvm/llvm-project/pull/146933
>From 0e9eab649f7a515c0697c3fe58309c478108f6b1 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Wed, 2 Jul 2025 09:43:32 -0500
Subject: [PATCH 01/12] [Frontend][OpenMP] Implement directive name parser
Implement a state machine that consumes tokens (words delimited by white
space), and returns the corresponding directive id, or fails if the tokens
did not form a valid name.
---
.../Frontend/OpenMP/DirectiveNameParser.h | 76 ++++++++
llvm/lib/Frontend/OpenMP/CMakeLists.txt | 1 +
.../Frontend/OpenMP/DirectiveNameParser.cpp | 93 ++++++++++
llvm/unittests/Frontend/CMakeLists.txt | 1 +
.../OpenMPDirectiveNameParserTest.cpp | 171 ++++++++++++++++++
5 files changed, 342 insertions(+)
create mode 100644 llvm/include/llvm/Frontend/OpenMP/DirectiveNameParser.h
create mode 100644 llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp
create mode 100644 llvm/unittests/Frontend/OpenMPDirectiveNameParserTest.cpp
diff --git a/llvm/include/llvm/Frontend/OpenMP/DirectiveNameParser.h b/llvm/include/llvm/Frontend/OpenMP/DirectiveNameParser.h
new file mode 100644
index 0000000000000..db8986601b2ca
--- /dev/null
+++ b/llvm/include/llvm/Frontend/OpenMP/DirectiveNameParser.h
@@ -0,0 +1,76 @@
+//===- DirectiveNameParser.h ------------------------------------- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_FRONTEND_OPENMP_DIRECTIVENAMEPARSER_H
+#define LLVM_FRONTEND_OPENMP_DIRECTIVENAMEPARSER_H
+
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Frontend/OpenMP/OMP.h"
+
+#include <memory>
+
+namespace llvm::omp {
+/// Parser class for OpenMP directive names. It only recognizes names listed
+/// in OMP.td, in particular it does not recognize Fortran's end-directives
+/// if they are not explicitly listed in OMP.td.
+///
+/// The class itself may be a singleton, once it's constructed it never
+/// changes.
+///
+/// Usage:
+/// {
+/// DirectiveNameParser Parser; // Could be static const.
+///
+/// DirectiveNameParser::State *S = Parser.initial();
+/// for (StringRef Token : Tokens)
+/// S = Parser.apply(S, Token); // Passing nullptr is ok.
+///
+/// if (S == nullptr) {
+/// // Error: ended up in a state from which there is no possible path
+/// // to a successful parse.
+/// } else if (S->Value == OMPD_unknown)
+/// // Parsed a sequence of tokens that are not a complete name, but
+/// // parsing more tokens could lead to a successful parse.
+/// } else {
+/// // Success.
+/// ParsedId = S->Value;
+/// }
+/// }
+struct DirectiveNameParser {
+ DirectiveNameParser(SourceLanguage L = SourceLanguage::C);
+
+ struct State {
+ Directive Value = Directive::OMPD_unknown;
+
+ private:
+ using TransitionMapTy = StringMap<State>;
+ std::unique_ptr<TransitionMapTy> Transition;
+
+ State *next(StringRef Tok);
+ bool isValid() const {
+ return Value != Directive::OMPD_unknown || !Transition->empty();
+ }
+ friend struct DirectiveNameParser;
+ };
+
+ const State *initial() const { return &InitialState; }
+ const State *apply(const State *Current, StringRef Tok) const;
+
+ static SmallVector<StringRef> tokenize(StringRef N);
+
+private:
+ void insertName(StringRef Name, Directive D);
+ State *insertTransition(State *From, StringRef Tok);
+
+ State InitialState;
+};
+} // namespace llvm::omp
+
+#endif // LLVM_FRONTEND_OPENMP_DIRECTIVENAMEPARSER_H
diff --git a/llvm/lib/Frontend/OpenMP/CMakeLists.txt b/llvm/lib/Frontend/OpenMP/CMakeLists.txt
index 5bf15ca3a8991..e60b59c1203b9 100644
--- a/llvm/lib/Frontend/OpenMP/CMakeLists.txt
+++ b/llvm/lib/Frontend/OpenMP/CMakeLists.txt
@@ -2,6 +2,7 @@ add_llvm_component_library(LLVMFrontendOpenMP
OMP.cpp
OMPContext.cpp
OMPIRBuilder.cpp
+ DirectiveNameParser.cpp
ADDITIONAL_HEADER_DIRS
${LLVM_MAIN_INCLUDE_DIR}/llvm/Frontend
diff --git a/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp b/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp
new file mode 100644
index 0000000000000..02ff8327a3054
--- /dev/null
+++ b/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp
@@ -0,0 +1,93 @@
+//===- DirectiveNameParser.cpp --------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Frontend/OpenMP/DirectiveNameParser.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Frontend/OpenMP/OMP.h"
+
+#include <cassert>
+#include <memory>
+
+namespace llvm::omp {
+DirectiveNameParser::DirectiveNameParser(SourceLanguage L) {
+ // Take every directive, get its name in every version, break the name up
+ // into whitespace-separated tokens, and insert each token.
+ for (size_t I = 0, E = Directive_enumSize; I != E; ++I) {
+ auto D = static_cast<Directive>(I);
+ if (D == Directive::OMPD_unknown || !(getDirectiveLanguages(D) & L))
+ continue;
+ for (unsigned Ver : getOpenMPVersions())
+ insertName(getOpenMPDirectiveName(D, Ver), D);
+ }
+}
+
+const DirectiveNameParser::State *
+DirectiveNameParser::apply(const State *Current, StringRef Tok) const {
+ if (!Current)
+ return Current;
+ assert(Current->isValid() && "Invalid input state");
+ if (const State *Next = const_cast<State *>(Current)->next(Tok))
+ return Next->isValid() ? Next : nullptr;
+ return nullptr;
+}
+
+SmallVector<StringRef> DirectiveNameParser::tokenize(StringRef Str) {
+ SmallVector<StringRef> Tokens;
+
+ auto nextChar = [](StringRef N, size_t I) {
+ while (I < N.size() && N[I] == ' ')
+ ++I;
+ return I;
+ };
+ auto nextSpace = [](StringRef N, size_t I) {
+ size_t S = N.find(' ', I);
+ return S != StringRef::npos ? S : N.size();
+ };
+
+ size_t From = nextChar(Str, 0);
+ size_t To = 0;
+
+ while (From != Str.size()) {
+ To = nextSpace(Str, From);
+ Tokens.push_back(Str.substr(From, To - From));
+ From = nextChar(Str, To);
+ }
+
+ return Tokens;
+}
+
+void DirectiveNameParser::insertName(StringRef Name, Directive D) {
+ State *Where = &InitialState;
+
+ for (StringRef Tok : tokenize(Name))
+ Where = insertTransition(Where, Tok);
+
+ Where->Value = D;
+}
+
+DirectiveNameParser::State *
+DirectiveNameParser::insertTransition(State *From, StringRef Tok) {
+ assert(From && "Expecting state");
+ if (!From->Transition) {
+ From->Transition = std::make_unique<State::TransitionMapTy>();
+ }
+ if (State *Next = From->next(Tok))
+ return Next;
+
+ auto [Where, DidIt] = From->Transition->try_emplace(Tok, State());
+ assert(DidIt && "Map insertion failed");
+ return &Where->second;
+}
+
+DirectiveNameParser::State *DirectiveNameParser::State::next(StringRef Tok) {
+ if (!Transition)
+ return nullptr;
+ auto F = Transition->find(Tok);
+ return F != Transition->end() ? &F->second : nullptr;
+}
+} // namespace llvm::omp
diff --git a/llvm/unittests/Frontend/CMakeLists.txt b/llvm/unittests/Frontend/CMakeLists.txt
index 2412cc9d26c7a..281d509227a46 100644
--- a/llvm/unittests/Frontend/CMakeLists.txt
+++ b/llvm/unittests/Frontend/CMakeLists.txt
@@ -20,6 +20,7 @@ add_llvm_unittest(LLVMFrontendTests
OpenMPCompositionTest.cpp
OpenMPDecompositionTest.cpp
OpenMPDirectiveNameTest.cpp
+ OpenMPDirectiveNameParserTest.cpp
DEPENDS
acc_gen
diff --git a/llvm/unittests/Frontend/OpenMPDirectiveNameParserTest.cpp b/llvm/unittests/Frontend/OpenMPDirectiveNameParserTest.cpp
new file mode 100644
index 0000000000000..11fef684dec4c
--- /dev/null
+++ b/llvm/unittests/Frontend/OpenMPDirectiveNameParserTest.cpp
@@ -0,0 +1,171 @@
+//===- llvm/unittests/Frontend/OpenMPDirectiveNameParserTest.cpp ----------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/Sequence.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Frontend/OpenMP/DirectiveNameParser.h"
+#include "llvm/Frontend/OpenMP/OMP.h"
+#include "gtest/gtest.h"
+
+#include <cctype>
+#include <sstream>
+#include <string>
+#include <tuple>
+#include <vector>
+
+using namespace llvm;
+
+static const omp::DirectiveNameParser &getParser() {
+ static omp::DirectiveNameParser Parser(omp::SourceLanguage::C |
+ omp::SourceLanguage::Fortran);
+ return Parser;
+}
+
+static std::vector<std::string> tokenize(StringRef S) {
+ std::vector<std::string> Tokens;
+
+ using TokenIterator = std::istream_iterator<std::string>;
+ std::string Copy = S.str();
+ std::istringstream Stream(Copy);
+
+ for (auto I = TokenIterator(Stream), E = TokenIterator(); I != E; ++I)
+ Tokens.push_back(*I);
+ return Tokens;
+}
+
+static std::string &prepareParamName(std::string &Name) {
+ for (size_t I = 0, E = Name.size(); I != E; ++I) {
+ // The parameter name must only have alphanumeric characters.
+ if (!isalnum(Name[I]))
+ Name[I] = 'X';
+ }
+ return Name;
+}
+
+namespace llvm {
+template <> struct enum_iteration_traits<omp::Directive> {
+ static constexpr bool is_iterable = true;
+};
+} // namespace llvm
+
+// Test tokenizing.
+
+class Tokenize : public testing::TestWithParam<omp::Directive> {};
+
+static bool isEqual(const SmallVector<StringRef> &A,
+ const std::vector<std::string> &B) {
+ if (A.size() != B.size())
+ return false;
+
+ for (size_t I = 0, E = A.size(); I != E; ++I) {
+ if (A[I] != StringRef(B[I]))
+ return false;
+ }
+ return true;
+}
+
+TEST_P(Tokenize, T) {
+ omp::Directive DirId = GetParam();
+ StringRef Name = omp::getOpenMPDirectiveName(DirId, omp::FallbackVersion);
+
+ SmallVector<StringRef> tokens1 = omp::DirectiveNameParser::tokenize(Name);
+ std::vector<std::string> tokens2 = tokenize(Name);
+ ASSERT_TRUE(isEqual(tokens1, tokens2));
+}
+
+static std::string
+getParamName1(const testing::TestParamInfo<Tokenize::ParamType> &Info) {
+ omp::Directive DirId = Info.param;
+ std::string Name =
+ omp::getOpenMPDirectiveName(DirId, omp::FallbackVersion).str();
+ return prepareParamName(Name);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ DirectiveNameParserTest, Tokenize,
+ testing::ValuesIn(
+ llvm::enum_seq(static_cast<omp::Directive>(0),
+ static_cast<omp::Directive>(omp::Directive_enumSize))),
+ getParamName1);
+
+// Test parsing of valid names.
+
+using ValueType = std::tuple<omp::Directive, unsigned>;
+
+class ParseValid : public testing::TestWithParam<ValueType> {};
+
+TEST_P(ParseValid, T) {
+ auto [DirId, Version] = GetParam();
+ if (DirId == omp::Directive::OMPD_unknown)
+ return;
+
+ std::string Name = omp::getOpenMPDirectiveName(DirId, Version).str();
+
+ // Tokenize and parse
+ auto &Parser = getParser();
+ auto *State = Parser.initial();
+ ASSERT_TRUE(State != nullptr);
+
+ std::vector<std::string> Tokens = tokenize(Name);
+ for (auto &Tok : Tokens) {
+ State = Parser.apply(State, Tok);
+ ASSERT_TRUE(State != nullptr);
+ }
+
+ ASSERT_EQ(State->Value, DirId);
+}
+
+static std::string
+getParamName2(const testing::TestParamInfo<ParseValid::ParamType> &Info) {
+ auto [DirId, Version] = Info.param;
+ std::string Name = omp::getOpenMPDirectiveName(DirId, Version).str() + "v" +
+ std::to_string(Version);
+ return prepareParamName(Name);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ DirectiveNameParserTest, ParseValid,
+ testing::Combine(testing::ValuesIn(llvm::enum_seq(
+ static_cast<omp::Directive>(0),
+ static_cast<omp::Directive>(omp::Directive_enumSize))),
+ testing::ValuesIn(omp::getOpenMPVersions())),
+ getParamName2);
+
+// Test parsing of invalid names
+
+class ParseInvalid : public testing::TestWithParam<std::string> {};
+
+TEST_P(ParseInvalid, T) {
+ std::string Name = GetParam();
+
+ auto &Parser = getParser();
+ auto *State = Parser.initial();
+ ASSERT_TRUE(State != nullptr);
+
+ std::vector<std::string> Tokens = tokenize(Name);
+ for (auto &Tok : Tokens)
+ State = Parser.apply(State, Tok);
+
+ ASSERT_TRUE(State == nullptr || State->Value == omp::Directive::OMPD_unknown);
+}
+
+namespace {
+using namespace std;
+
+INSTANTIATE_TEST_SUITE_P(DirectiveNameParserTest, ParseInvalid,
+ testing::Values(
+ // Names that contain invalid tokens
+ "bad"s, "target teams invalid"s,
+ "target sections parallel"s,
+ "target teams distribute parallel for wrong"s,
+ // Valid beginning, but not a complete name
+ "begin declare"s,
+ // Complete name with extra tokens
+ "distribute simd target"s));
+} // namespace
>From ab3f0cc9d240f12e07be452effa75b5c7d010d9b Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Wed, 2 Jul 2025 11:25:00 -0500
Subject: [PATCH 02/12] [clang][OpenMP] Use DirectiveNameParser to parse
directive names
This simplifies the parsing code in clang quite a bit.
---
clang/lib/Parse/ParseOpenMP.cpp | 181 ++++----------------------------
1 file changed, 19 insertions(+), 162 deletions(-)
diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp
index f694ae1d0d112..c0a17d0e9537d 100644
--- a/clang/lib/Parse/ParseOpenMP.cpp
+++ b/clang/lib/Parse/ParseOpenMP.cpp
@@ -25,6 +25,7 @@
#include "clang/Sema/SemaOpenMP.h"
#include "llvm/ADT/SmallBitVector.h"
#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Frontend/OpenMP/DirectiveNameParser.h"
#include "llvm/Frontend/OpenMP/OMPAssume.h"
#include "llvm/Frontend/OpenMP/OMPContext.h"
#include <optional>
@@ -37,48 +38,6 @@ using namespace llvm::omp;
//===----------------------------------------------------------------------===//
namespace {
-enum OpenMPDirectiveKindEx {
- OMPD_cancellation = llvm::omp::Directive_enumSize + 1,
- OMPD_data,
- OMPD_declare,
- OMPD_end,
- OMPD_end_declare,
- OMPD_enter,
- OMPD_exit,
- OMPD_point,
- OMPD_reduction,
- OMPD_target_enter,
- OMPD_target_exit,
- OMPD_update,
- OMPD_distribute_parallel,
- OMPD_teams_distribute_parallel,
- OMPD_target_teams_distribute_parallel,
- OMPD_mapper,
- OMPD_variant,
- OMPD_begin,
- OMPD_begin_declare,
-};
-
-// Helper to unify the enum class OpenMPDirectiveKind with its extension
-// the OpenMPDirectiveKindEx enum which allows to use them together as if they
-// are unsigned values.
-struct OpenMPDirectiveKindExWrapper {
- OpenMPDirectiveKindExWrapper(unsigned Value) : Value(Value) {}
- OpenMPDirectiveKindExWrapper(OpenMPDirectiveKind DK) : Value(unsigned(DK)) {}
- bool operator==(OpenMPDirectiveKindExWrapper V) const {
- return Value == V.Value;
- }
- bool operator!=(OpenMPDirectiveKindExWrapper V) const {
- return Value != V.Value;
- }
- bool operator==(OpenMPDirectiveKind V) const { return Value == unsigned(V); }
- bool operator!=(OpenMPDirectiveKind V) const { return Value != unsigned(V); }
- bool operator<(OpenMPDirectiveKind V) const { return Value < unsigned(V); }
- operator unsigned() const { return Value; }
- operator OpenMPDirectiveKind() const { return OpenMPDirectiveKind(Value); }
- unsigned Value;
-};
-
class DeclDirectiveListParserHelper final {
SmallVector<Expr *, 4> Identifiers;
Parser *P;
@@ -97,130 +56,32 @@ class DeclDirectiveListParserHelper final {
};
} // namespace
-// Map token string to extended OMP token kind that are
-// OpenMPDirectiveKind + OpenMPDirectiveKindEx.
-static unsigned getOpenMPDirectiveKindEx(StringRef S) {
- OpenMPDirectiveKindExWrapper DKind = getOpenMPDirectiveKind(S);
- if (DKind != OMPD_unknown)
- return DKind;
-
- return llvm::StringSwitch<OpenMPDirectiveKindExWrapper>(S)
- .Case("cancellation", OMPD_cancellation)
- .Case("data", OMPD_data)
- .Case("declare", OMPD_declare)
- .Case("end", OMPD_end)
- .Case("enter", OMPD_enter)
- .Case("exit", OMPD_exit)
- .Case("point", OMPD_point)
- .Case("reduction", OMPD_reduction)
- .Case("update", OMPD_update)
- .Case("mapper", OMPD_mapper)
- .Case("variant", OMPD_variant)
- .Case("begin", OMPD_begin)
- .Default(OMPD_unknown);
-}
+static OpenMPDirectiveKind parseOpenMPDirectiveKind(Parser &P) {
+ static const DirectiveNameParser DNP;
+
+ const DirectiveNameParser::State *S = DNP.initial();
-static OpenMPDirectiveKindExWrapper parseOpenMPDirectiveKind(Parser &P) {
- // Array of foldings: F[i][0] F[i][1] ===> F[i][2].
- // E.g.: OMPD_for OMPD_simd ===> OMPD_for_simd
- // TODO: add other combined directives in topological order.
- static const OpenMPDirectiveKindExWrapper F[][3] = {
- {OMPD_begin, OMPD_declare, OMPD_begin_declare},
- {OMPD_begin, OMPD_assumes, OMPD_begin_assumes},
- {OMPD_end, OMPD_declare, OMPD_end_declare},
- {OMPD_end, OMPD_assumes, OMPD_end_assumes},
- {OMPD_cancellation, OMPD_point, OMPD_cancellation_point},
- {OMPD_declare, OMPD_reduction, OMPD_declare_reduction},
- {OMPD_declare, OMPD_mapper, OMPD_declare_mapper},
- {OMPD_declare, OMPD_simd, OMPD_declare_simd},
- {OMPD_declare, OMPD_target, OMPD_declare_target},
- {OMPD_declare, OMPD_variant, OMPD_declare_variant},
- {OMPD_begin_declare, OMPD_target, OMPD_begin_declare_target},
- {OMPD_begin_declare, OMPD_variant, OMPD_begin_declare_variant},
- {OMPD_end_declare, OMPD_variant, OMPD_end_declare_variant},
- {OMPD_distribute, OMPD_parallel, OMPD_distribute_parallel},
- {OMPD_distribute_parallel, OMPD_for, OMPD_distribute_parallel_for},
- {OMPD_distribute_parallel_for, OMPD_simd,
- OMPD_distribute_parallel_for_simd},
- {OMPD_distribute, OMPD_simd, OMPD_distribute_simd},
- {OMPD_end_declare, OMPD_target, OMPD_end_declare_target},
- {OMPD_target, OMPD_data, OMPD_target_data},
- {OMPD_target, OMPD_enter, OMPD_target_enter},
- {OMPD_target, OMPD_exit, OMPD_target_exit},
- {OMPD_target, OMPD_update, OMPD_target_update},
- {OMPD_target_enter, OMPD_data, OMPD_target_enter_data},
- {OMPD_target_exit, OMPD_data, OMPD_target_exit_data},
- {OMPD_for, OMPD_simd, OMPD_for_simd},
- {OMPD_parallel, OMPD_for, OMPD_parallel_for},
- {OMPD_parallel_for, OMPD_simd, OMPD_parallel_for_simd},
- {OMPD_parallel, OMPD_loop, OMPD_parallel_loop},
- {OMPD_parallel, OMPD_sections, OMPD_parallel_sections},
- {OMPD_taskloop, OMPD_simd, OMPD_taskloop_simd},
- {OMPD_target, OMPD_parallel, OMPD_target_parallel},
- {OMPD_target, OMPD_simd, OMPD_target_simd},
- {OMPD_target_parallel, OMPD_loop, OMPD_target_parallel_loop},
- {OMPD_target_parallel, OMPD_for, OMPD_target_parallel_for},
- {OMPD_target_parallel_for, OMPD_simd, OMPD_target_parallel_for_simd},
- {OMPD_teams, OMPD_distribute, OMPD_teams_distribute},
- {OMPD_teams_distribute, OMPD_simd, OMPD_teams_distribute_simd},
- {OMPD_teams_distribute, OMPD_parallel, OMPD_teams_distribute_parallel},
- {OMPD_teams_distribute_parallel, OMPD_for,
- OMPD_teams_distribute_parallel_for},
- {OMPD_teams_distribute_parallel_for, OMPD_simd,
- OMPD_teams_distribute_parallel_for_simd},
- {OMPD_teams, OMPD_loop, OMPD_teams_loop},
- {OMPD_target, OMPD_teams, OMPD_target_teams},
- {OMPD_target_teams, OMPD_distribute, OMPD_target_teams_distribute},
- {OMPD_target_teams, OMPD_loop, OMPD_target_teams_loop},
- {OMPD_target_teams_distribute, OMPD_parallel,
- OMPD_target_teams_distribute_parallel},
- {OMPD_target_teams_distribute, OMPD_simd,
- OMPD_target_teams_distribute_simd},
- {OMPD_target_teams_distribute_parallel, OMPD_for,
- OMPD_target_teams_distribute_parallel_for},
- {OMPD_target_teams_distribute_parallel_for, OMPD_simd,
- OMPD_target_teams_distribute_parallel_for_simd},
- {OMPD_master, OMPD_taskloop, OMPD_master_taskloop},
- {OMPD_masked, OMPD_taskloop, OMPD_masked_taskloop},
- {OMPD_master_taskloop, OMPD_simd, OMPD_master_taskloop_simd},
- {OMPD_masked_taskloop, OMPD_simd, OMPD_masked_taskloop_simd},
- {OMPD_parallel, OMPD_master, OMPD_parallel_master},
- {OMPD_parallel, OMPD_masked, OMPD_parallel_masked},
- {OMPD_parallel_master, OMPD_taskloop, OMPD_parallel_master_taskloop},
- {OMPD_parallel_masked, OMPD_taskloop, OMPD_parallel_masked_taskloop},
- {OMPD_parallel_master_taskloop, OMPD_simd,
- OMPD_parallel_master_taskloop_simd},
- {OMPD_parallel_masked_taskloop, OMPD_simd,
- OMPD_parallel_masked_taskloop_simd}};
- enum { CancellationPoint = 0, DeclareReduction = 1, TargetData = 2 };
Token Tok = P.getCurToken();
- OpenMPDirectiveKindExWrapper DKind =
- Tok.isAnnotation()
- ? static_cast<unsigned>(OMPD_unknown)
- : getOpenMPDirectiveKindEx(P.getPreprocessor().getSpelling(Tok));
- if (DKind == OMPD_unknown)
+ if (Tok.isAnnotation())
return OMPD_unknown;
- for (const auto &I : F) {
- if (DKind != I[0])
- continue;
+ S = DNP.apply(S, P.getPreprocessor().getSpelling(Tok));
+ if (S == nullptr)
+ return OMPD_unknown;
+ while (!Tok.isAnnotation()) {
+ OpenMPDirectiveKind DKind = S->Value;
Tok = P.getPreprocessor().LookAhead(0);
- OpenMPDirectiveKindExWrapper SDKind =
- Tok.isAnnotation()
- ? static_cast<unsigned>(OMPD_unknown)
- : getOpenMPDirectiveKindEx(P.getPreprocessor().getSpelling(Tok));
- if (SDKind == OMPD_unknown)
- continue;
-
- if (SDKind == I[1]) {
+ if (!Tok.isAnnotation()) {
+ S = DNP.apply(S, P.getPreprocessor().getSpelling(Tok));
+ if (S == nullptr)
+ return DKind;
P.ConsumeToken();
- DKind = I[2];
}
}
- return unsigned(DKind) < llvm::omp::Directive_enumSize
- ? static_cast<OpenMPDirectiveKind>(DKind)
- : OMPD_unknown;
+
+ assert(S && "Should have exited early");
+ return S->Value;
}
static DeclarationName parseOpenMPReductionId(Parser &P) {
@@ -2629,10 +2490,6 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective(
Diag(Tok, diag::err_omp_unknown_directive);
return StmtError();
}
- if (!(getDirectiveLanguages(DKind) & SourceLanguage::C)) {
- // Treat directives that are not allowed in C/C++ as unknown.
- DKind = OMPD_unknown;
- }
StmtResult Directive = StmtError();
@@ -4014,7 +3871,7 @@ OMPClause *Parser::ParseOpenMPSingleExprWithArgClause(OpenMPDirectiveKind DKind,
KLoc.push_back(Tok.getLocation());
TentativeParsingAction TPA(*this);
auto DK = parseOpenMPDirectiveKind(*this);
- Arg.push_back(DK);
+ Arg.push_back(static_cast<unsigned>(DK));
if (DK != OMPD_unknown) {
ConsumeToken();
if (Tok.is(tok::colon) && getLangOpts().OpenMP > 40) {
>From f49ba8b84a8218e52c09713be49a4a04bfa36920 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Wed, 2 Jul 2025 16:49:32 -0500
Subject: [PATCH 03/12] Use llvm::seq
---
llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp b/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp
index 02ff8327a3054..b560858b44df6 100644
--- a/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp
+++ b/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "llvm/Frontend/OpenMP/DirectiveNameParser.h"
+#include "llvm/ADT/Sequence.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Frontend/OpenMP/OMP.h"
@@ -17,7 +18,7 @@ namespace llvm::omp {
DirectiveNameParser::DirectiveNameParser(SourceLanguage L) {
// Take every directive, get its name in every version, break the name up
// into whitespace-separated tokens, and insert each token.
- for (size_t I = 0, E = Directive_enumSize; I != E; ++I) {
+ for (size_t I : llvm::seq<size_t>(Directive_enumSize)) {
auto D = static_cast<Directive>(I);
if (D == Directive::OMPD_unknown || !(getDirectiveLanguages(D) & L))
continue;
>From 538cfcdd285c34d08ff6a67253e20343547982f1 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Wed, 2 Jul 2025 16:49:36 -0500
Subject: [PATCH 04/12] Add const version of next()
---
.../include/llvm/Frontend/OpenMP/DirectiveNameParser.h | 1 +
llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp | 10 +++++++++-
2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/llvm/include/llvm/Frontend/OpenMP/DirectiveNameParser.h b/llvm/include/llvm/Frontend/OpenMP/DirectiveNameParser.h
index db8986601b2ca..9996375c9023e 100644
--- a/llvm/include/llvm/Frontend/OpenMP/DirectiveNameParser.h
+++ b/llvm/include/llvm/Frontend/OpenMP/DirectiveNameParser.h
@@ -54,6 +54,7 @@ struct DirectiveNameParser {
std::unique_ptr<TransitionMapTy> Transition;
State *next(StringRef Tok);
+ const State *next(StringRef Tok) const;
bool isValid() const {
return Value != Directive::OMPD_unknown || !Transition->empty();
}
diff --git a/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp b/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp
index b560858b44df6..4aaf25cc32e05 100644
--- a/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp
+++ b/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp
@@ -32,7 +32,7 @@ DirectiveNameParser::apply(const State *Current, StringRef Tok) const {
if (!Current)
return Current;
assert(Current->isValid() && "Invalid input state");
- if (const State *Next = const_cast<State *>(Current)->next(Tok))
+ if (const State *Next = Current->next(Tok))
return Next->isValid() ? Next : nullptr;
return nullptr;
}
@@ -85,6 +85,14 @@ DirectiveNameParser::insertTransition(State *From, StringRef Tok) {
return &Where->second;
}
+const DirectiveNameParser::State *
+DirectiveNameParser::State::next(StringRef Tok) const {
+ if (!Transition)
+ return nullptr;
+ auto F = Transition->find(Tok);
+ return F != Transition->end() ? &F->second : nullptr;
+}
+
DirectiveNameParser::State *DirectiveNameParser::State::next(StringRef Tok) {
if (!Transition)
return nullptr;
>From 9d35e461829665e3eb1d9c4cc03548b02b2c0925 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Wed, 2 Jul 2025 16:52:09 -0500
Subject: [PATCH 05/12] Add missing brace in comment
---
llvm/include/llvm/Frontend/OpenMP/DirectiveNameParser.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/include/llvm/Frontend/OpenMP/DirectiveNameParser.h b/llvm/include/llvm/Frontend/OpenMP/DirectiveNameParser.h
index 9996375c9023e..b1ff34c003295 100644
--- a/llvm/include/llvm/Frontend/OpenMP/DirectiveNameParser.h
+++ b/llvm/include/llvm/Frontend/OpenMP/DirectiveNameParser.h
@@ -35,7 +35,7 @@ namespace llvm::omp {
/// if (S == nullptr) {
/// // Error: ended up in a state from which there is no possible path
/// // to a successful parse.
-/// } else if (S->Value == OMPD_unknown)
+/// } else if (S->Value == OMPD_unknown) {
/// // Parsed a sequence of tokens that are not a complete name, but
/// // parsing more tokens could lead to a successful parse.
/// } else {
>From 5d2a8a367eb4e13b298897f85120e785dd97e39b Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Thu, 3 Jul 2025 10:03:58 -0500
Subject: [PATCH 06/12] Rename apply to consume
---
llvm/include/llvm/Frontend/OpenMP/DirectiveNameParser.h | 4 ++--
llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp | 2 +-
llvm/unittests/Frontend/OpenMPDirectiveNameParserTest.cpp | 4 ++--
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/llvm/include/llvm/Frontend/OpenMP/DirectiveNameParser.h b/llvm/include/llvm/Frontend/OpenMP/DirectiveNameParser.h
index b1ff34c003295..898c32aa4b8f2 100644
--- a/llvm/include/llvm/Frontend/OpenMP/DirectiveNameParser.h
+++ b/llvm/include/llvm/Frontend/OpenMP/DirectiveNameParser.h
@@ -30,7 +30,7 @@ namespace llvm::omp {
///
/// DirectiveNameParser::State *S = Parser.initial();
/// for (StringRef Token : Tokens)
-/// S = Parser.apply(S, Token); // Passing nullptr is ok.
+/// S = Parser.consume(S, Token); // Passing nullptr is ok.
///
/// if (S == nullptr) {
/// // Error: ended up in a state from which there is no possible path
@@ -62,7 +62,7 @@ struct DirectiveNameParser {
};
const State *initial() const { return &InitialState; }
- const State *apply(const State *Current, StringRef Tok) const;
+ const State *consume(const State *Current, StringRef Tok) const;
static SmallVector<StringRef> tokenize(StringRef N);
diff --git a/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp b/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp
index 4aaf25cc32e05..037ffe2c13f1e 100644
--- a/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp
+++ b/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp
@@ -28,7 +28,7 @@ DirectiveNameParser::DirectiveNameParser(SourceLanguage L) {
}
const DirectiveNameParser::State *
-DirectiveNameParser::apply(const State *Current, StringRef Tok) const {
+DirectiveNameParser::consume(const State *Current, StringRef Tok) const {
if (!Current)
return Current;
assert(Current->isValid() && "Invalid input state");
diff --git a/llvm/unittests/Frontend/OpenMPDirectiveNameParserTest.cpp b/llvm/unittests/Frontend/OpenMPDirectiveNameParserTest.cpp
index 11fef684dec4c..0363a08cc0f03 100644
--- a/llvm/unittests/Frontend/OpenMPDirectiveNameParserTest.cpp
+++ b/llvm/unittests/Frontend/OpenMPDirectiveNameParserTest.cpp
@@ -114,7 +114,7 @@ TEST_P(ParseValid, T) {
std::vector<std::string> Tokens = tokenize(Name);
for (auto &Tok : Tokens) {
- State = Parser.apply(State, Tok);
+ State = Parser.consume(State, Tok);
ASSERT_TRUE(State != nullptr);
}
@@ -150,7 +150,7 @@ TEST_P(ParseInvalid, T) {
std::vector<std::string> Tokens = tokenize(Name);
for (auto &Tok : Tokens)
- State = Parser.apply(State, Tok);
+ State = Parser.consume(State, Tok);
ASSERT_TRUE(State == nullptr || State->Value == omp::Directive::OMPD_unknown);
}
>From cde46dc3d14d224c4f4f1ec9ef8eafb4d9d12828 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Thu, 3 Jul 2025 10:04:23 -0500
Subject: [PATCH 07/12] Use isspace instead of ' '
---
.../Frontend/OpenMP/DirectiveNameParser.cpp | 18 ++++++++++--------
1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp b/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp
index 037ffe2c13f1e..d42a3e98adb37 100644
--- a/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp
+++ b/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp
@@ -12,6 +12,7 @@
#include "llvm/Frontend/OpenMP/OMP.h"
#include <cassert>
+#include <cctype>
#include <memory>
namespace llvm::omp {
@@ -40,23 +41,24 @@ DirectiveNameParser::consume(const State *Current, StringRef Tok) const {
SmallVector<StringRef> DirectiveNameParser::tokenize(StringRef Str) {
SmallVector<StringRef> Tokens;
- auto nextChar = [](StringRef N, size_t I) {
- while (I < N.size() && N[I] == ' ')
+ auto NextChar = [](StringRef N, size_t I) {
+ while (I < N.size() && isspace(N[I]))
++I;
return I;
};
- auto nextSpace = [](StringRef N, size_t I) {
- size_t S = N.find(' ', I);
- return S != StringRef::npos ? S : N.size();
+ auto NextSpace = [](StringRef N, size_t I) {
+ while (I < N.size() && !isspace(N[I]))
+ ++I;
+ return I;
};
- size_t From = nextChar(Str, 0);
+ size_t From = NextChar(Str, 0);
size_t To = 0;
while (From != Str.size()) {
- To = nextSpace(Str, From);
+ To = NextSpace(Str, From);
Tokens.push_back(Str.substr(From, To - From));
- From = nextChar(Str, To);
+ From = NextChar(Str, To);
}
return Tokens;
>From e86ac032518655d19f0f5b00d6987a20edbf42ba Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Thu, 3 Jul 2025 10:40:35 -0500
Subject: [PATCH 08/12] Use StringSplit
---
.../Frontend/OpenMP/DirectiveNameParser.cpp | 24 ++-----------------
1 file changed, 2 insertions(+), 22 deletions(-)
diff --git a/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp b/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp
index d42a3e98adb37..2246fed78aa72 100644
--- a/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp
+++ b/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp
@@ -8,11 +8,11 @@
#include "llvm/Frontend/OpenMP/DirectiveNameParser.h"
#include "llvm/ADT/Sequence.h"
+#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Frontend/OpenMP/OMP.h"
#include <cassert>
-#include <cctype>
#include <memory>
namespace llvm::omp {
@@ -40,27 +40,7 @@ DirectiveNameParser::consume(const State *Current, StringRef Tok) const {
SmallVector<StringRef> DirectiveNameParser::tokenize(StringRef Str) {
SmallVector<StringRef> Tokens;
-
- auto NextChar = [](StringRef N, size_t I) {
- while (I < N.size() && isspace(N[I]))
- ++I;
- return I;
- };
- auto NextSpace = [](StringRef N, size_t I) {
- while (I < N.size() && !isspace(N[I]))
- ++I;
- return I;
- };
-
- size_t From = NextChar(Str, 0);
- size_t To = 0;
-
- while (From != Str.size()) {
- To = NextSpace(Str, From);
- Tokens.push_back(Str.substr(From, To - From));
- From = NextChar(Str, To);
- }
-
+ SplitString(Str, Tokens);
return Tokens;
}
>From 2768326ecd62c2831cda66f1123f302c377fe79d Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Thu, 3 Jul 2025 10:42:06 -0500
Subject: [PATCH 09/12] Remove {} around single statement
---
llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp b/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp
index 2246fed78aa72..62e24825111f6 100644
--- a/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp
+++ b/llvm/lib/Frontend/OpenMP/DirectiveNameParser.cpp
@@ -56,9 +56,8 @@ void DirectiveNameParser::insertName(StringRef Name, Directive D) {
DirectiveNameParser::State *
DirectiveNameParser::insertTransition(State *From, StringRef Tok) {
assert(From && "Expecting state");
- if (!From->Transition) {
+ if (!From->Transition)
From->Transition = std::make_unique<State::TransitionMapTy>();
- }
if (State *Next = From->next(Tok))
return Next;
>From ca31af4bbb783cbecab3d4c7af3c2f240e435fc1 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Thu, 3 Jul 2025 11:16:03 -0500
Subject: [PATCH 10/12] Use consume instead ofof apply
---
clang/lib/Parse/ParseOpenMP.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp
index c0a17d0e9537d..c3c6149d9de1a 100644
--- a/clang/lib/Parse/ParseOpenMP.cpp
+++ b/clang/lib/Parse/ParseOpenMP.cpp
@@ -65,7 +65,7 @@ static OpenMPDirectiveKind parseOpenMPDirectiveKind(Parser &P) {
if (Tok.isAnnotation())
return OMPD_unknown;
- S = DNP.apply(S, P.getPreprocessor().getSpelling(Tok));
+ S = DNP.consume(S, P.getPreprocessor().getSpelling(Tok));
if (S == nullptr)
return OMPD_unknown;
@@ -73,7 +73,7 @@ static OpenMPDirectiveKind parseOpenMPDirectiveKind(Parser &P) {
OpenMPDirectiveKind DKind = S->Value;
Tok = P.getPreprocessor().LookAhead(0);
if (!Tok.isAnnotation()) {
- S = DNP.apply(S, P.getPreprocessor().getSpelling(Tok));
+ S = DNP.consume(S, P.getPreprocessor().getSpelling(Tok));
if (S == nullptr)
return DKind;
P.ConsumeToken();
>From 49ef393290309ea9c0caed965b0b182c02356529 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Thu, 3 Jul 2025 12:18:55 -0500
Subject: [PATCH 11/12] Rename DNP to DirParser
---
clang/lib/Parse/ParseOpenMP.cpp | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp
index c3c6149d9de1a..5256d08259b60 100644
--- a/clang/lib/Parse/ParseOpenMP.cpp
+++ b/clang/lib/Parse/ParseOpenMP.cpp
@@ -57,15 +57,15 @@ class DeclDirectiveListParserHelper final {
} // namespace
static OpenMPDirectiveKind parseOpenMPDirectiveKind(Parser &P) {
- static const DirectiveNameParser DNP;
+ static const DirectiveNameParser DirParser;
- const DirectiveNameParser::State *S = DNP.initial();
+ const DirectiveNameParser::State *S = DirParser.initial();
Token Tok = P.getCurToken();
if (Tok.isAnnotation())
return OMPD_unknown;
- S = DNP.consume(S, P.getPreprocessor().getSpelling(Tok));
+ S = DirParser.consume(S, P.getPreprocessor().getSpelling(Tok));
if (S == nullptr)
return OMPD_unknown;
@@ -73,7 +73,7 @@ static OpenMPDirectiveKind parseOpenMPDirectiveKind(Parser &P) {
OpenMPDirectiveKind DKind = S->Value;
Tok = P.getPreprocessor().LookAhead(0);
if (!Tok.isAnnotation()) {
- S = DNP.consume(S, P.getPreprocessor().getSpelling(Tok));
+ S = DirParser.consume(S, P.getPreprocessor().getSpelling(Tok));
if (S == nullptr)
return DKind;
P.ConsumeToken();
>From 5ad103e08e8a06cfc3708ba83601e073a022bb7e Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Wed, 2 Jul 2025 12:49:04 -0500
Subject: [PATCH 12/12] [clang][OpenMP] Issue a warning when parsing future
directive spelling
OpenMP 6.0 introduced alternative spelling for some directives, with the
previous spellings still being allowed.
Warn the user when a new spelling is encountered with OpenMP version set
to an older value.
---
clang/include/clang/Basic/DiagnosticGroups.td | 4 +-
.../clang/Basic/DiagnosticParseKinds.td | 3 +
clang/lib/Parse/ParseOpenMP.cpp | 28 ++++++++--
.../test/OpenMP/openmp-6-future-spellings.cpp | 55 +++++++++++++++++++
4 files changed, 85 insertions(+), 5 deletions(-)
create mode 100644 clang/test/OpenMP/openmp-6-future-spellings.cpp
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 36fa3227fd6a6..ace8663b73a4a 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1530,9 +1530,11 @@ def OpenMPPre51Compat : DiagGroup<"pre-openmp-51-compat">;
def OpenMP51Ext : DiagGroup<"openmp-51-extensions">;
def OpenMPExtensions : DiagGroup<"openmp-extensions">;
def OpenMPTargetException : DiagGroup<"openmp-target-exception">;
+def OpenMPFuture : DiagGroup<"openmp-future">;
def OpenMP : DiagGroup<"openmp", [
SourceUsesOpenMP, OpenMPClauses, OpenMPLoopForm, OpenMPTarget,
- OpenMPMapping, OpenMP51Ext, OpenMPExtensions, OpenMPTargetException
+ OpenMPMapping, OpenMP51Ext, OpenMPExtensions, OpenMPTargetException,
+ OpenMPFuture
]>;
// OpenACC warnings.
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 6c30da376dafb..87eb2b724b297 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1488,6 +1488,9 @@ def err_omp_multiple_step_or_linear_modifier : Error<
"multiple %select{'step size'|'linear modifier'}0 found in linear clause">;
def err_omp_deprecate_old_syntax: Error<
"old syntax '%0' on '%1' clause was deprecated, use new syntax '%2'">;
+def warn_omp_future_directive_spelling: Warning<
+ "directive spelling '%0' is introduced in a later OpenMP version">,
+ InGroup<OpenMPFuture>;
def warn_pragma_expected_colon_r_paren : Warning<
"missing ':' or ')' after %0 - ignoring">, InGroup<IgnoredPragmas>;
def err_omp_unknown_directive : Error<
diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp
index 5256d08259b60..cb9eb3304c317 100644
--- a/clang/lib/Parse/ParseOpenMP.cpp
+++ b/clang/lib/Parse/ParseOpenMP.cpp
@@ -56,6 +56,21 @@ class DeclDirectiveListParserHelper final {
};
} // namespace
+static OpenMPDirectiveKind checkOpenMPDirectiveName(Parser &P,
+ SourceLocation Loc,
+ OpenMPDirectiveKind Kind,
+ StringRef Name) {
+ unsigned Version = P.getLangOpts().OpenMP;
+ auto [D, VR] = getOpenMPDirectiveKindAndVersions(Name);
+ assert(D == Kind && "Directive kind mismatch");
+ // Ignore the case Version > VR.Max: In OpenMP 6.0 all prior spellings
+ // are explicitly allowed.
+ if (Version < VR.Min)
+ P.Diag(Loc, diag::warn_omp_future_directive_spelling) << Name;
+
+ return Kind;
+}
+
static OpenMPDirectiveKind parseOpenMPDirectiveKind(Parser &P) {
static const DirectiveNameParser DirParser;
@@ -65,7 +80,10 @@ static OpenMPDirectiveKind parseOpenMPDirectiveKind(Parser &P) {
if (Tok.isAnnotation())
return OMPD_unknown;
- S = DirParser.consume(S, P.getPreprocessor().getSpelling(Tok));
+ std::string Concat = P.getPreprocessor().getSpelling(Tok);
+ SourceLocation Loc = Tok.getLocation();
+
+ S = DirParser.consume(S, Concat);
if (S == nullptr)
return OMPD_unknown;
@@ -73,15 +91,17 @@ static OpenMPDirectiveKind parseOpenMPDirectiveKind(Parser &P) {
OpenMPDirectiveKind DKind = S->Value;
Tok = P.getPreprocessor().LookAhead(0);
if (!Tok.isAnnotation()) {
- S = DirParser.consume(S, P.getPreprocessor().getSpelling(Tok));
+ std::string TS = P.getPreprocessor().getSpelling(Tok);
+ S = DirParser.consume(S, TS);
if (S == nullptr)
- return DKind;
+ return checkOpenMPDirectiveName(P, Loc, DKind, Concat);
+ Concat += ' ' + TS;
P.ConsumeToken();
}
}
assert(S && "Should have exited early");
- return S->Value;
+ return checkOpenMPDirectiveName(P, Loc, S->Value, Concat);
}
static DeclarationName parseOpenMPReductionId(Parser &P) {
diff --git a/clang/test/OpenMP/openmp-6-future-spellings.cpp b/clang/test/OpenMP/openmp-6-future-spellings.cpp
new file mode 100644
index 0000000000000..642ed3502d475
--- /dev/null
+++ b/clang/test/OpenMP/openmp-6-future-spellings.cpp
@@ -0,0 +1,55 @@
+// RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=52 -ferror-limit 100 -o - %s
+
+// expected-warning at +1 {{directive spelling 'begin declare_target' is introduced in a later OpenMP version}}
+#pragma omp begin declare_target
+void f0();
+// expected-warning at +1 {{directive spelling 'end declare_target' is introduced in a later OpenMP version}}
+#pragma omp end declare_target
+
+// expected-warning at +1 {{directive spelling 'begin declare_variant' is introduced in a later OpenMP version}}
+#pragma omp begin declare_variant match(user={condition(true)})
+void f1();
+// expected-warning at +1 {{directive spelling 'end declare_variant' is introduced in a later OpenMP version}}
+#pragma omp end declare_variant
+
+int x;
+// expected-warning at +1 {{directive spelling 'declare_target' is introduced in a later OpenMP version}}
+#pragma omp declare_target(x)
+
+struct A {
+ int x, y;
+};
+// expected-warning at +1 {{directive spelling 'declare_mapper' is introduced in a later OpenMP version}}
+#pragma omp declare_mapper(mymapper: A a) map(tofrom:a.x, a.y)
+A add(A, A);
+// expected-warning at +1 {{directive spelling 'declare_reduction' is introduced in a later OpenMP version}}
+#pragma omp declare_reduction(+: A: omp_out = add(omp_in, omp_out))
+
+// expected-warning at +1 {{directive spelling 'declare_simd' is introduced in a later OpenMP version}}
+#pragma omp declare_simd
+void f2();
+
+void g3();
+// expected-warning at +1 {{directive spelling 'declare_variant' is introduced in a later OpenMP version}}
+#pragma omp declare_variant(g3) match(user={condition(true)})
+void f3() {}
+
+void fred() {
+ #pragma omp parallel
+ {
+ // expected-warning at +1 {{directive spelling 'cancellation_point' is introduced in a later OpenMP version}}
+ #pragma omp cancellation_point parallel
+ }
+
+ // expected-warning at +1 {{directive spelling 'target_data' is introduced in a later OpenMP version}}
+ #pragma omp target_data map(tofrom: x)
+ {}
+
+ // expected-warning at +1 {{directive spelling 'target_enter_data' is introduced in a later OpenMP version}}
+ #pragma omp target_enter_data map(to: x)
+ // expected-warning at +1 {{directive spelling 'target_exit_data' is introduced in a later OpenMP version}}
+ #pragma omp target_exit_data map(from: x)
+ // expected-warning at +1 {{directive spelling 'target_update' is introduced in a later OpenMP version}}
+ #pragma omp target_update from(x)
+}
+
More information about the llvm-commits
mailing list