Skip to content

[OpenMP][clang] 6.0: parsing/sema for message/severity for parallel #146093

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

ro-i
Copy link
Contributor

@ro-i ro-i commented Jun 27, 2025

Implement parsing and semantic analysis support for the message and severity clauses that have been added to the parallel directive in OpenMP 6.0, 12.1.

Implement parsing and semantic analysis support for the message and
severity clauses that have been added to the parallel directive in
OpenMP 6.0, 12.1.
@ro-i ro-i requested review from alexey-bataev and kparzysz June 27, 2025 15:27
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" flang:openmp clang:openmp OpenMP related changes to Clang labels Jun 27, 2025
@llvmbot
Copy link
Member

llvmbot commented Jun 27, 2025

@llvm/pr-subscribers-flang-openmp

@llvm/pr-subscribers-clang

Author: Robert Imschweiler (ro-i)

Changes

Implement parsing and semantic analysis support for the message and severity clauses that have been added to the parallel directive in OpenMP 6.0, 12.1.


Full diff: https://github.com/llvm/llvm-project/pull/146093.diff

6 Files Affected:

  • (modified) clang/include/clang/AST/OpenMPClause.h (+4-2)
  • (modified) clang/lib/Sema/SemaOpenMP.cpp (+2-2)
  • (modified) clang/test/OpenMP/parallel_ast_print.cpp (+12-12)
  • (added) clang/test/OpenMP/parallel_message_messages.cpp (+89)
  • (added) clang/test/OpenMP/parallel_severity_messages.cpp (+70)
  • (modified) llvm/include/llvm/Frontend/OpenMP/OMP.td (+2)
diff --git a/clang/include/clang/AST/OpenMPClause.h b/clang/include/clang/AST/OpenMPClause.h
index c6f99fb21a0f0..8b602f49aefde 100644
--- a/clang/include/clang/AST/OpenMPClause.h
+++ b/clang/include/clang/AST/OpenMPClause.h
@@ -1775,7 +1775,8 @@ class OMPAtClause final : public OMPClause {
   }
 };
 
-/// This represents 'severity' clause in the '#pragma omp error' directive
+/// This represents the 'severity' clause in the '#pragma omp error' and the
+/// '#pragma omp parallel' directives.
 ///
 /// \code
 /// #pragma omp error severity(fatal)
@@ -1855,7 +1856,8 @@ class OMPSeverityClause final : public OMPClause {
   }
 };
 
-/// This represents 'message' clause in the '#pragma omp error' directive
+/// This represents the 'message' clause in the '#pragma omp error' and the
+/// '#pragma omp parallel' directives.
 ///
 /// \code
 /// #pragma omp error message("GNU compiler required.")
diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index a30acbe9a4bca..4ecc9b0d4c5c8 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -6620,6 +6620,8 @@ StmtResult SemaOpenMP::ActOnOpenMPExecutableDirective(
       case OMPC_affinity:
       case OMPC_bind:
       case OMPC_filter:
+      case OMPC_severity:
+      case OMPC_message:
         continue;
       case OMPC_allocator:
       case OMPC_flush:
@@ -6637,8 +6639,6 @@ StmtResult SemaOpenMP::ActOnOpenMPExecutableDirective(
       case OMPC_match:
       case OMPC_when:
       case OMPC_at:
-      case OMPC_severity:
-      case OMPC_message:
       default:
         llvm_unreachable("Unexpected clause");
       }
diff --git a/clang/test/OpenMP/parallel_ast_print.cpp b/clang/test/OpenMP/parallel_ast_print.cpp
index 948baaff30d89..15439ea31215a 100644
--- a/clang/test/OpenMP/parallel_ast_print.cpp
+++ b/clang/test/OpenMP/parallel_ast_print.cpp
@@ -173,13 +173,13 @@ T tmain(T argc, T *argv) {
   foo();
 #endif
 #ifdef OMP60
-#pragma omp parallel default(none), private(argc,b) firstprivate(argv) shared (d) if (parallel:argc > 0) num_threads(strict: C) copyin(S<T>::TS, thrp) proc_bind(primary) reduction(+:c, arr1[argc]) reduction(max:e, arr[:C][0:10])
+#pragma omp parallel default(none), private(argc,b) firstprivate(argv) shared (d) if (parallel:argc > 0) num_threads(strict: C) copyin(S<T>::TS, thrp) proc_bind(primary) reduction(+:c, arr1[argc]) reduction(max:e, arr[:C][0:10]) message("msg") severity(fatal)
   foo();
 #endif
 #pragma omp parallel if (C) num_threads(s) proc_bind(close) reduction(^:e, f, arr[0:C][:argc]) reduction(default, && : g) reduction(task,+:argc)
   foo();
 #ifdef OMP60
-#pragma omp parallel if (C) num_threads(strict: s) proc_bind(close) reduction(^:e, f, arr[0:C][:argc]) reduction(default, && : g) reduction(task,+:argc)
+#pragma omp parallel if (C) num_threads(strict: s) proc_bind(close) reduction(^:e, f, arr[0:C][:argc]) reduction(default, && : g) reduction(task,+:argc) message("msg") severity(warning)
   foo();
 #endif
   return 0;
@@ -196,11 +196,11 @@ T tmain(T argc, T *argv) {
 // CHECK-NEXT: foo()
 // OMP51-NEXT: #pragma omp parallel default(none) private(argc,b) firstprivate(argv) shared(d) if(parallel: argc > 0) num_threads(C) copyin(S<T>::TS,thrp) proc_bind(primary) reduction(+: c,arr1[argc]) reduction(max: e,arr[:C][0:10])
 // OMP51-NEXT: foo()
-// OMP60-NEXT: #pragma omp parallel default(none) private(argc,b) firstprivate(argv) shared(d) if(parallel: argc > 0) num_threads(strict: C) copyin(S<T>::TS,thrp) proc_bind(primary) reduction(+: c,arr1[argc]) reduction(max: e,arr[:C][0:10])
+// OMP60-NEXT: #pragma omp parallel default(none) private(argc,b) firstprivate(argv) shared(d) if(parallel: argc > 0) num_threads(strict: C) copyin(S<T>::TS,thrp) proc_bind(primary) reduction(+: c,arr1[argc]) reduction(max: e,arr[:C][0:10]) message("msg") severity(fatal)
 // OMP60-NEXT: foo()
 // CHECK-NEXT: #pragma omp parallel if(C) num_threads(s) proc_bind(close) reduction(^: e,f,arr[0:C][:argc]) reduction(default, &&: g) reduction(task, +: argc)
 // CHECK-NEXT: foo()
-// OMP60-NEXT: #pragma omp parallel if(C) num_threads(strict: s) proc_bind(close) reduction(^: e,f,arr[0:C][:argc]) reduction(default, &&: g) reduction(task, +: argc)
+// OMP60-NEXT: #pragma omp parallel if(C) num_threads(strict: s) proc_bind(close) reduction(^: e,f,arr[0:C][:argc]) reduction(default, &&: g) reduction(task, +: argc) message("msg") severity(warning)
 // OMP60-NEXT: foo()
 // CHECK: template<> int tmain<int, 5>(int argc, int *argv) {
 // CHECK-NEXT: int b = argc, c, d, e, f, g;
@@ -213,11 +213,11 @@ T tmain(T argc, T *argv) {
 // CHECK-NEXT: foo()
 // OMP51-NEXT: #pragma omp parallel default(none) private(argc,b) firstprivate(argv) shared(d) if(parallel: argc > 0) num_threads(5) copyin(S<int>::TS,thrp) proc_bind(primary) reduction(+: c,arr1[argc]) reduction(max: e,arr[:5][0:10])
 // OMP51-NEXT: foo()
-// OMP60-NEXT: #pragma omp parallel default(none) private(argc,b) firstprivate(argv) shared(d) if(parallel: argc > 0) num_threads(strict: 5) copyin(S<int>::TS,thrp) proc_bind(primary) reduction(+: c,arr1[argc]) reduction(max: e,arr[:5][0:10])
+// OMP60-NEXT: #pragma omp parallel default(none) private(argc,b) firstprivate(argv) shared(d) if(parallel: argc > 0) num_threads(strict: 5) copyin(S<int>::TS,thrp) proc_bind(primary) reduction(+: c,arr1[argc]) reduction(max: e,arr[:5][0:10]) message("msg") severity(fatal)
 // OMP60-NEXT: foo()
 // CHECK-NEXT: #pragma omp parallel if(5) num_threads(s) proc_bind(close) reduction(^: e,f,arr[0:5][:argc]) reduction(default, &&: g) reduction(task, +: argc)
 // CHECK-NEXT: foo()
-// OMP60-NEXT: #pragma omp parallel if(5) num_threads(strict: s) proc_bind(close) reduction(^: e,f,arr[0:5][:argc]) reduction(default, &&: g) reduction(task, +: argc)
+// OMP60-NEXT: #pragma omp parallel if(5) num_threads(strict: s) proc_bind(close) reduction(^: e,f,arr[0:5][:argc]) reduction(default, &&: g) reduction(task, +: argc) message("msg") severity(warning)
 // OMP60-NEXT: foo()
 // CHECK: template<> long tmain<long, 1>(long argc, long *argv) {
 // CHECK-NEXT: long b = argc, c, d, e, f, g;
@@ -230,11 +230,11 @@ T tmain(T argc, T *argv) {
 // CHECK-NEXT: foo()
 // OMP51-NEXT: #pragma omp parallel default(none) private(argc,b) firstprivate(argv) shared(d) if(parallel: argc > 0) num_threads(1) copyin(S<long>::TS,thrp) proc_bind(primary) reduction(+: c,arr1[argc]) reduction(max: e,arr[:1][0:10])
 // OMP51-NEXT: foo()
-// OMP60-NEXT: #pragma omp parallel default(none) private(argc,b) firstprivate(argv) shared(d) if(parallel: argc > 0) num_threads(strict: 1) copyin(S<long>::TS,thrp) proc_bind(primary) reduction(+: c,arr1[argc]) reduction(max: e,arr[:1][0:10])
+// OMP60-NEXT: #pragma omp parallel default(none) private(argc,b) firstprivate(argv) shared(d) if(parallel: argc > 0) num_threads(strict: 1) copyin(S<long>::TS,thrp) proc_bind(primary) reduction(+: c,arr1[argc]) reduction(max: e,arr[:1][0:10]) message("msg") severity(fatal)
 // OMP60-NEXT: foo()
 // CHECK-NEXT: #pragma omp parallel if(1) num_threads(s) proc_bind(close) reduction(^: e,f,arr[0:1][:argc]) reduction(default, &&: g) reduction(task, +: argc)
 // CHECK-NEXT: foo()
-// OMP60-NEXT: #pragma omp parallel if(1) num_threads(strict: s) proc_bind(close) reduction(^: e,f,arr[0:1][:argc]) reduction(default, &&: g) reduction(task, +: argc)
+// OMP60-NEXT: #pragma omp parallel if(1) num_threads(strict: s) proc_bind(close) reduction(^: e,f,arr[0:1][:argc]) reduction(default, &&: g) reduction(task, +: argc) message("msg") severity(warning)
 // OMP60-NEXT: foo()
 
 enum Enum { };
@@ -256,8 +256,8 @@ int main (int argc, char **argv) {
   foo();
 // CHECK-NEXT: foo();
 #ifdef OMP60
-#pragma omp parallel default(none), private(argc,b) firstprivate(argv) if (parallel: argc > 0) num_threads(strict: ee) copyin(a) proc_bind(spread) reduction(| : c, d, arr1[argc]) reduction(* : e, arr[:10][0:argc]) allocate(e)
-// OMP60-NEXT: #pragma omp parallel default(none) private(argc,b) firstprivate(argv) if(parallel: argc > 0) num_threads(strict: ee) copyin(a) proc_bind(spread) reduction(|: c,d,arr1[argc]) reduction(*: e,arr[:10][0:argc]) allocate(e)
+#pragma omp parallel default(none), private(argc,b) firstprivate(argv) if (parallel: argc > 0) num_threads(strict: ee) copyin(a) proc_bind(spread) reduction(| : c, d, arr1[argc]) reduction(* : e, arr[:10][0:argc]) allocate(e) message("msg") severity(fatal)
+// OMP60-NEXT: #pragma omp parallel default(none) private(argc,b) firstprivate(argv) if(parallel: argc > 0) num_threads(strict: ee) copyin(a) proc_bind(spread) reduction(|: c,d,arr1[argc]) reduction(*: e,arr[:10][0:argc]) allocate(e) message("msg") severity(fatal)
   foo();
 // OMP60-NEXT: foo();
 #endif
@@ -266,8 +266,8 @@ int main (int argc, char **argv) {
   foo();
 // CHECK-NEXT: foo()
 #ifdef OMP60
-#pragma omp parallel allocate(e) if (b) num_threads(strict: c) proc_bind(close) reduction(^:e, f) reduction(&& : g, arr[0:argc][:10])
-// OMP60-NEXT: #pragma omp parallel allocate(e) if(b) num_threads(strict: c) proc_bind(close) reduction(^: e,f) reduction(&&: g,arr[0:argc][:10])
+#pragma omp parallel allocate(e) if (b) num_threads(strict: c) proc_bind(close) reduction(^:e, f) reduction(&& : g, arr[0:argc][:10]) message("msg") severity(warning)
+// OMP60-NEXT: #pragma omp parallel allocate(e) if(b) num_threads(strict: c) proc_bind(close) reduction(^: e,f) reduction(&&: g,arr[0:argc][:10]) message("msg") severity(warning)
   foo();
 // OMP60-NEXT: foo()
 #endif
diff --git a/clang/test/OpenMP/parallel_message_messages.cpp b/clang/test/OpenMP/parallel_message_messages.cpp
new file mode 100644
index 0000000000000..470fadc032280
--- /dev/null
+++ b/clang/test/OpenMP/parallel_message_messages.cpp
@@ -0,0 +1,89 @@
+// RUN: %clang_cc1 -verify=expected -fopenmp -fopenmp-version=60 -ferror-limit 100 %s -Wuninitialized
+// RUN: %clang_cc1 -verify=expected -fopenmp-simd -fopenmp-version=60 -ferror-limit 100 %s -Wuninitialized
+
+void foo() {}
+
+template <class T, typename S, int N>
+T tmain(T argc, S **argv) {
+  // Correct usage
+  #pragma omp parallel message("correct message")
+
+  // Missing parentheses
+  #pragma omp parallel message // expected-error {{expected '(' after 'message'}}
+  
+  // Empty parentheses
+  #pragma omp parallel message() // expected-error {{expected expression}}
+
+  // Non-string literal
+  #pragma omp parallel message(123) // expected-warning {{expected string literal in 'clause message' - ignoring}}
+  #pragma omp parallel message(argc) // expected-warning {{expected string literal in 'clause message' - ignoring}}
+  #pragma omp parallel message(argv[0]) // expected-warning {{expected string literal in 'clause message' - ignoring}}
+
+  // Multiple arguments
+  #pragma omp parallel message("msg1", "msg2") // expected-error {{expected ')'}} expected-note {{to match this '('}}
+  
+  // Unterminated string
+  // expected-error@+1 {{expected expression}} expected-error@+1 {{expected ')'}} expected-warning@+1 {{missing terminating '"' character}} expected-note@+1 {{to match this '('}}
+  #pragma omp parallel message("unterminated
+
+  // Unterminated clause
+  // expected-error@+1 {{expected ')'}} expected-note@+1 {{to match this '('}}
+  #pragma omp parallel message("msg"
+
+  // Extra tokens after clause
+  #pragma omp parallel message("msg") extra // expected-warning {{extra tokens at the end of '#pragma omp parallel' are ignored}}
+
+  // Multiple message clauses
+  #pragma omp parallel message("msg1") message("msg2") // expected-error {{directive '#pragma omp parallel' cannot contain more than one 'message' clause}}
+
+  // Message clause with other clauses (should be valid, but test for interaction)
+  #pragma omp parallel message("msg") num_threads(2)
+
+  // Message clause with invalid clause
+  #pragma omp parallel message("msg") invalid_clause // expected-warning {{extra tokens at the end of '#pragma omp parallel' are ignored}}
+
+  // Message clause with missing string and other clause
+  #pragma omp parallel message() num_threads(2) // expected-error {{expected expression}}
+
+  // Message clause with macro that is not a string
+  #define NOT_A_STRING 123
+  #pragma omp parallel message(NOT_A_STRING) // expected-warning {{expected string literal in 'clause message' - ignoring}}
+
+  // Message clause with template parameter that is not a string
+  #pragma omp parallel message(N) // expected-warning {{expected string literal in 'clause message' - ignoring}}
+
+  // Message clause with macro that is a string
+  #define A_STRING "macro string"
+  #pragma omp parallel message(A_STRING)
+
+  // Message clause with concatenated string literals
+  #pragma omp parallel message("hello" " world")
+
+  // Message clause with wide string literal
+  #pragma omp parallel message(L"wide string")
+
+  // Message clause with UTF-8 string literal
+  #pragma omp parallel message(u8"utf8 string")
+
+  // Message clause with raw string literal
+  #pragma omp parallel message(R"(raw string)")
+
+  foo();
+
+  return argc;
+}
+
+int main(int argc, char **argv) {
+  // Correct usage
+  #pragma omp parallel message("main correct")
+
+  // Invalid: missing string
+  #pragma omp parallel message() // expected-error {{expression}}
+
+  // Invalid: non-string
+  #pragma omp parallel message(argc) // expected-warning {{expected string literal in 'clause message' - ignoring}}
+
+  foo();
+
+  return tmain<int, char, 3>(argc, argv);
+}
diff --git a/clang/test/OpenMP/parallel_severity_messages.cpp b/clang/test/OpenMP/parallel_severity_messages.cpp
new file mode 100644
index 0000000000000..b1cff762d9bd8
--- /dev/null
+++ b/clang/test/OpenMP/parallel_severity_messages.cpp
@@ -0,0 +1,70 @@
+// RUN: %clang_cc1 -verify=expected -fopenmp -fopenmp-version=60 -ferror-limit 100 %s -Wuninitialized
+// RUN: %clang_cc1 -verify=expected -fopenmp-simd -fopenmp-version=60 -ferror-limit 100 %s -Wuninitialized
+
+void foo() {}
+
+template <class T, typename S, int N>
+T tmain(T argc, S **argv) {
+  // Correct usages
+  #pragma omp parallel severity(fatal)
+  #pragma omp parallel severity(warning)
+
+  // Missing parentheses
+  #pragma omp parallel severity // expected-error {{expected '(' after 'severity'}}
+
+  // Empty parentheses
+  #pragma omp parallel severity() // expected-error {{expected 'fatal' or 'warning' in OpenMP clause 'severity'}}
+
+  // Invalid value
+  #pragma omp parallel severity(error) // expected-error {{expected 'fatal' or 'warning' in OpenMP clause 'severity'}}
+  #pragma omp parallel severity(unknown) // expected-error {{expected 'fatal' or 'warning' in OpenMP clause 'severity'}}
+
+  // Multiple arguments
+  #pragma omp parallel severity(fatal, warning) // expected-error {{expected ')'}} expected-note {{to match this '('}}
+
+  // Unterminated clause
+  // expected-error@+1 {{expected ')'}} expected-note@+1 {{to match this '('}}
+  #pragma omp parallel severity(fatal
+
+  // Extra tokens after clause
+  #pragma omp parallel severity(fatal) extra // expected-warning {{extra tokens at the end of '#pragma omp parallel' are ignored}}
+
+  // Multiple severity clauses
+  #pragma omp parallel severity(fatal) severity(warning) // expected-error {{directive '#pragma omp parallel' cannot contain more than one 'severity' clause}}
+
+  // Severity clause with other clauses (should be valid)
+  #pragma omp parallel severity(warning) num_threads(2)
+
+  // Severity clause with invalid clause
+  #pragma omp parallel severity(fatal) invalid_clause // expected-warning {{extra tokens at the end of '#pragma omp parallel' are ignored}}
+
+  // Severity clause with macro that is not a valid value
+  #define NOT_A_SEVERITY 123
+  #pragma omp parallel severity(NOT_A_SEVERITY) // expected-error {{expected 'fatal' or 'warning' in OpenMP clause 'severity'}}
+
+  // Severity clause with macro that is a valid value
+  #define FATAL fatal
+  #pragma omp parallel severity(FATAL)
+
+  // Severity clause with template parameter that is not a valid value
+  #pragma omp parallel severity(N) // expected-error {{expected 'fatal' or 'warning' in OpenMP clause 'severity'}}
+
+  foo();
+
+  return argc;
+}
+
+int main(int argc, char **argv) {
+  // Correct usage
+  #pragma omp parallel severity(fatal)
+
+  // Invalid: missing value
+  #pragma omp parallel severity() // expected-error {{expected 'fatal' or 'warning' in OpenMP clause 'severity'}}
+
+  // Invalid: non-keyword
+  #pragma omp parallel severity(argc) // expected-error {{expected 'fatal' or 'warning' in OpenMP clause 'severity'}}
+
+  foo();
+
+  return tmain<int, char, 3>(argc, argv);
+}
diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td
index f2610011a7e04..f218d1f813533 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMP.td
+++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td
@@ -960,6 +960,8 @@ def OMP_Parallel : Directive<[Spelling<"parallel">]> {
     VersionedClause<OMPC_If>,
     VersionedClause<OMPC_NumThreads>,
     VersionedClause<OMPC_ProcBind>,
+    VersionedClause<OMPC_Message>,
+    VersionedClause<OMPC_Severity>,
   ];
   let association = AS_Block;
   let category = CA_Executable;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:openmp OpenMP related changes to Clang clang Clang issues not falling into any other category flang:openmp
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants