[clang] f9cea2f - [Clang][OpenMP] Fix crash on `atomic` with an erroneous statement (#228914)

via cfe-commits cfe-commits at lists.llvm.org
Sun Oct 18 11:08:05 PDT 2026


Author: Akash Manna
Date: 2026-10-18T23:37:59+05:30
New Revision: f9cea2f24b6713cc344a875e674d45728c19d24e

URL: https://github.com/llvm/llvm-project/commit/f9cea2f24b6713cc344a875e674d45728c19d24e
DIFF: https://github.com/llvm/llvm-project/commit/f9cea2f24b6713cc344a875e674d45728c19d24e.diff

LOG: [Clang][OpenMP] Fix crash on `atomic` with an erroneous statement (#228914)

Fixes #107979

A statement under `#pragma omp atomic` that already has an error, such
as `0--;` or an assignment to a `const` variable, reaches
`OpenMPAtomicUpdateChecker` as a `RecoveryExpr`. Since #71480 the
checker reports it with a `NotAValidExpression` code, but that code is
also used as the `%select` index of `note_omp_atomic_update`, which only
had eight options. So for `atomic`, `atomic update` and the `v = expr`
form of `atomic capture` the note was emitted with index 8 and we hit
the assertion in `HandleSelectModifier`.

The note now has an option for `NotAValidExpression`, so every error
code of the checker has a matching message. The checker also no longer
builds the helper update expression when an operand contains errors,
which used to add a second error, `invalid operands to binary expression
('int' and '<dependent type>')`, for statements such as `x += 0--;`.

Assisted-by : AI tools

Added: 
    

Modified: 
    clang/docs/ReleaseNotes.md
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/lib/Sema/SemaOpenMP.cpp
    clang/test/OpenMP/atomic_messages.c
    clang/test/OpenMP/atomic_messages.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 7705b0e61bf4b..7d6347414cc5c 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -1313,6 +1313,8 @@ The `alpha.cplusplus.UseAfterLifetimeEnd` checker was renamed to `alpha.core.Use
 - Added the OpenMP 6.1 `#pragma omp flatten` loop transformation and the
   `depth` clause. Flatten combines perfectly nested canonical loops into one
   loop. `depth(k)` selects how many outermost loops to combine (default 2).
+- Fixed a crash when the statement of an OpenMP `atomic` directive contains an
+  invalid expression, such as `0--;`. (#GH107979)
 - Canonicalize intra-tiles in loop tiling. `#pragma omp tile` still emits a
   min-bounded inner loop, which vectorizes well. When a parent directive such as
   `for collapse(n)` needs a constant per-tile trip count, Clang rereads a

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 3e8ba5bf88a42..494d7bcd95309 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12428,7 +12428,8 @@ def err_omp_atomic_not_expression_statement : Error<
 def note_omp_atomic_update: Note<
   "%select{expected an expression statement|expected built-in binary or unary operator|expected unary decrement/increment operation|"
   "expected expression of scalar type|expected assignment expression|expected built-in binary operator|"
-  "expected one of '+', '*', '-', '/', '&', '^', '%|', '<<', or '>>' built-in operations|expected in right hand side of expression}0">;
+  "expected one of '+', '*', '-', '/', '&', '^', '%|', '<<', or '>>' built-in operations|expected in right hand side of expression|"
+  "expected a valid expression}0">;
 def err_omp_atomic_capture_not_expression_statement : Error<
   "the statement for 'atomic capture' must be an expression statement of form 'v = ++x;', 'v = --x;', 'v = x++;', 'v = x--;', 'v = x binop= expr;', 'v = x = x binop expr' or 'v = x = expr binop x',"
   " where x and v are both lvalue expressions with scalar type">;

diff  --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index a3be416d2da24..efe8c9338b5a9 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -12669,7 +12669,8 @@ bool OpenMPAtomicUpdateChecker::checkStatement(Stmt *S, unsigned DiagId,
   }
   if (SemaRef.CurContext->isDependentContext())
     E = X = UpdateExpr = nullptr;
-  if (ErrorFound == NoError && E && X) {
+  if (ErrorFound == NoError && E && X && !E->containsErrors() &&
+      !X->containsErrors()) {
     // Build an update expression of form 'OpaqueValueExpr(x) binop
     // OpaqueValueExpr(expr)' or 'OpaqueValueExpr(expr) binop
     // OpaqueValueExpr(x)' and then cast it to the type of the 'x' expression.

diff  --git a/clang/test/OpenMP/atomic_messages.c b/clang/test/OpenMP/atomic_messages.c
index f4e7db52494af..9d42e21984e77 100644
--- a/clang/test/OpenMP/atomic_messages.c
+++ b/clang/test/OpenMP/atomic_messages.c
@@ -1,10 +1,10 @@
-// RUN: %clang_cc1 -verify=expected,omp45 -fopenmp -fopenmp-version=45 -ferror-limit 100 %s -Wuninitialized
-// RUN: %clang_cc1 -verify=expected,omp50 -fopenmp -fopenmp-version=50 -ferror-limit 100 %s -Wuninitialized
-// RUN: %clang_cc1 -DOMP51 -verify=expected,omp50,omp51 -fopenmp -ferror-limit 100 %s -Wuninitialized
+// RUN: %clang_cc1 -verify=expected,omp45 -fopenmp -fopenmp-version=45 -ferror-limit 150 %s -Wuninitialized
+// RUN: %clang_cc1 -verify=expected,omp50 -fopenmp -fopenmp-version=50 -ferror-limit 150 %s -Wuninitialized
+// RUN: %clang_cc1 -DOMP51 -verify=expected,omp50,omp51 -fopenmp -ferror-limit 150 %s -Wuninitialized
 
-// RUN: %clang_cc1 -verify=expected,omp45 -fopenmp-simd -fopenmp-version=45 -ferror-limit 100 %s -Wuninitialized
-// RUN: %clang_cc1 -verify=expected,omp50 -fopenmp-simd -fopenmp-version=50 -ferror-limit 100 %s -Wuninitialized
-// RUN: %clang_cc1 -DOMP51 -verify=expected,omp50,omp51 -fopenmp-simd -ferror-limit 100 %s -Wuninitialized
+// RUN: %clang_cc1 -verify=expected,omp45 -fopenmp-simd -fopenmp-version=45 -ferror-limit 150 %s -Wuninitialized
+// RUN: %clang_cc1 -verify=expected,omp50 -fopenmp-simd -fopenmp-version=50 -ferror-limit 150 %s -Wuninitialized
+// RUN: %clang_cc1 -DOMP51 -verify=expected,omp50,omp51 -fopenmp-simd -ferror-limit 150 %s -Wuninitialized
 
 void xxx(int argc) {
   int x; // expected-note {{initialize the variable 'x' to silence this warning}}
@@ -397,6 +397,84 @@ void hint(void) {
   a += 1;
 }
 
+void gh107979(void) {
+  int x = 0, v = 0;
+#pragma omp atomic
+  // expected-error at +2 {{the statement for 'atomic' must be an expression statement of form '++x;', '--x;', 'x++;', 'x--;', 'x binop= expr;', 'x = x binop expr' or 'x = expr binop x', where x is an lvalue expression with scalar type}}
+  // expected-note at +1 {{expected a valid expression}}
+  0--; // expected-error {{expression is not assignable}}
+#pragma omp atomic
+  // expected-error at +2 {{the statement for 'atomic' must be an expression statement of form '++x;', '--x;', 'x++;', 'x--;', 'x binop= expr;', 'x = x binop expr' or 'x = expr binop x', where x is an lvalue expression with scalar type}}
+  // expected-note at +1 {{expected a valid expression}}
+  --0; // expected-error {{expression is not assignable}}
+#pragma omp atomic
+  // expected-error at +2 {{the statement for 'atomic' must be an expression statement of form '++x;', '--x;', 'x++;', 'x--;', 'x binop= expr;', 'x = x binop expr' or 'x = expr binop x', where x is an lvalue expression with scalar type}}
+  // expected-note at +1 {{expected a valid expression}}
+  0++; // expected-error {{expression is not assignable}}
+#pragma omp atomic update
+  // expected-error at +2 {{the statement for 'atomic update' must be an expression statement of form '++x;', '--x;', 'x++;', 'x--;', 'x binop= expr;', 'x = x binop expr' or 'x = expr binop x', where x is an lvalue expression with scalar type}}
+  // expected-note at +1 {{expected a valid expression}}
+  0 += 1; // expected-error {{expression is not assignable}}
+#pragma omp atomic capture
+  // expected-error at +2 {{the statement for 'atomic capture' must be an expression statement of form 'v = ++x;', 'v = --x;', 'v = x++;', 'v = x--;', 'v = x binop= expr;', 'v = x = x binop expr' or 'v = x = expr binop x', where x and v are both lvalue expressions with scalar type}}
+  // expected-note at +1 {{expected a valid expression}}
+  v = 0--; // expected-error {{expression is not assignable}}
+#pragma omp atomic
+  // expected-error at +2 {{the statement for 'atomic' must be an expression statement of form '++x;', '--x;', 'x++;', 'x--;', 'x binop= expr;', 'x = x binop expr' or 'x = expr binop x', where x is an lvalue expression with scalar type}}
+  // expected-note at +1 {{expected a valid expression}}
+  (void)(0--); // expected-error {{expression is not assignable}}
+#pragma omp atomic
+  // expected-error at +2 {{the statement for 'atomic' must be an expression statement of form '++x;', '--x;', 'x++;', 'x--;', 'x binop= expr;', 'x = x binop expr' or 'x = expr binop x', where x is an lvalue expression with scalar type}}
+  // expected-note at +1 {{expected a valid expression}}
+  x ? 0-- : 1; // expected-error {{expression is not assignable}}
+  const int c = 0; // expected-note {{variable 'c' declared const here}}
+#pragma omp atomic
+  // expected-error at +2 {{the statement for 'atomic' must be an expression statement of form '++x;', '--x;', 'x++;', 'x--;', 'x binop= expr;', 'x = x binop expr' or 'x = expr binop x', where x is an lvalue expression with scalar type}}
+  // expected-note at +1 {{expected a valid expression}}
+  c += 1; // expected-error {{cannot assign to variable 'c' with const-qualified type 'const int'}}
+#pragma omp atomic read
+  0--; // expected-error {{expression is not assignable}}
+#pragma omp atomic write
+  0--; // expected-error {{expression is not assignable}}
+#pragma omp atomic capture
+  { v = x; 0--; } // expected-error {{expression is not assignable}}
+#pragma omp atomic
+  x += 0--; // expected-error {{expression is not assignable}}
+#pragma omp atomic
+  x = x + 0--; // expected-error {{expression is not assignable}}
+#pragma omp atomic
+  (0--)++; // expected-error {{expression is not assignable}}
+#pragma omp atomic capture
+  v = x += 0--; // expected-error {{expression is not assignable}}
+#pragma omp atomic capture
+  { v = x; x += 0--; } // expected-error {{expression is not assignable}}
+#ifdef OMP51
+#pragma omp atomic compare
+  // omp51-error at +2 {{the statement for 'atomic compare' must be a compound statement of form '{x = expr ordop x ? expr : x;}', '{x = x ordop expr? expr : x;}', '{x = x == e ? d : x;}', '{x = e == x ? d : x;}', or 'if(expr ordop x) {x = expr;}', 'if(x ordop expr) {x = expr;}', 'if(x == e) {x = d;}', 'if(e == x) {x = d;}' where 'x' is an lvalue expression with scalar type, 'expr', 'e', and 'd' are expressions with scalar type, and 'ordop' is one of '<' or '>'}}
+  // omp51-note at +1 {{expected assignment statement}}
+  0--; // expected-error {{expression is not assignable}}
+#endif
+  int w = 0--; // expected-error {{expression is not assignable}}
+#pragma omp atomic
+  w++;
+#pragma omp atomic
+  w += 1;
+#pragma omp atomic
+  w = w + 1;
+#pragma omp atomic read
+  v = w;
+#pragma omp atomic write
+  w = 1;
+#pragma omp atomic capture
+  v = w++;
+#pragma omp atomic capture
+  { v = w; w++; }
+#pragma omp atomic capture
+  { w++; v = w; }
+#pragma omp atomic
+  x--;
+}
+
 #ifdef OMP51
 extern void bbar(void);
 extern int ffoo(void);

diff  --git a/clang/test/OpenMP/atomic_messages.cpp b/clang/test/OpenMP/atomic_messages.cpp
index c4e240a0ebb4e..4dd26967f0e3a 100644
--- a/clang/test/OpenMP/atomic_messages.cpp
+++ b/clang/test/OpenMP/atomic_messages.cpp
@@ -1,10 +1,10 @@
-// RUN: %clang_cc1 -verify=expected,omp45 -fopenmp -fopenmp-version=45 -ferror-limit 150 %s -Wuninitialized
-// RUN: %clang_cc1 -verify=expected,omp50 -fopenmp -fopenmp-version=50 -ferror-limit 150 %s -Wuninitialized
-// RUN: %clang_cc1 -DOMP51 -verify=expected,omp50,omp51 -fopenmp -ferror-limit 150 %s -Wuninitialized
+// RUN: %clang_cc1 -verify=expected,omp45 -fopenmp -fopenmp-version=45 -ferror-limit 200 %s -Wuninitialized
+// RUN: %clang_cc1 -verify=expected,omp50 -fopenmp -fopenmp-version=50 -ferror-limit 200 %s -Wuninitialized
+// RUN: %clang_cc1 -DOMP51 -verify=expected,omp50,omp51 -fopenmp -ferror-limit 200 %s -Wuninitialized
 
-// RUN: %clang_cc1 -verify=expected,omp45 -fopenmp-simd -fopenmp-version=45 -ferror-limit 150 %s -Wuninitialized
-// RUN: %clang_cc1 -verify=expected,omp50 -fopenmp-simd -fopenmp-version=50 -ferror-limit 150 %s -Wuninitialized
-// RUN: %clang_cc1 -DOMP51 -verify=expected,omp50,omp51 -fopenmp-simd -ferror-limit 150 %s -Wuninitialized
+// RUN: %clang_cc1 -verify=expected,omp45 -fopenmp-simd -fopenmp-version=45 -ferror-limit 200 %s -Wuninitialized
+// RUN: %clang_cc1 -verify=expected,omp50 -fopenmp-simd -fopenmp-version=50 -ferror-limit 200 %s -Wuninitialized
+// RUN: %clang_cc1 -DOMP51 -verify=expected,omp50,omp51 -fopenmp-simd -ferror-limit 200 %s -Wuninitialized
 
 int foo() {
 L1:
@@ -992,6 +992,93 @@ int mixed() {
   return mixed<int>();
 }
 
+void gh107979() {
+  int x = 0, v = 0;
+#pragma omp atomic
+  // expected-error at +2 {{the statement for 'atomic' must be an expression statement of form '++x;', '--x;', 'x++;', 'x--;', 'x binop= expr;', 'x = x binop expr' or 'x = expr binop x', where x is an lvalue expression with scalar type}}
+  // expected-note at +1 {{expected a valid expression}}
+  0--; // expected-error {{expression is not assignable}}
+#pragma omp atomic
+  // expected-error at +2 {{the statement for 'atomic' must be an expression statement of form '++x;', '--x;', 'x++;', 'x--;', 'x binop= expr;', 'x = x binop expr' or 'x = expr binop x', where x is an lvalue expression with scalar type}}
+  // expected-note at +1 {{expected a valid expression}}
+  --0; // expected-error {{expression is not assignable}}
+#pragma omp atomic
+  // expected-error at +2 {{the statement for 'atomic' must be an expression statement of form '++x;', '--x;', 'x++;', 'x--;', 'x binop= expr;', 'x = x binop expr' or 'x = expr binop x', where x is an lvalue expression with scalar type}}
+  // expected-note at +1 {{expected a valid expression}}
+  0++; // expected-error {{expression is not assignable}}
+#pragma omp atomic update
+  // expected-error at +2 {{the statement for 'atomic update' must be an expression statement of form '++x;', '--x;', 'x++;', 'x--;', 'x binop= expr;', 'x = x binop expr' or 'x = expr binop x', where x is an lvalue expression with scalar type}}
+  // expected-note at +1 {{expected a valid expression}}
+  0 += 1; // expected-error {{expression is not assignable}}
+#pragma omp atomic capture
+  // expected-error at +2 {{the statement for 'atomic capture' must be an expression statement of form 'v = ++x;', 'v = --x;', 'v = x++;', 'v = x--;', 'v = x binop= expr;', 'v = x = x binop expr' or 'v = x = expr binop x', where x and v are both lvalue expressions with scalar type}}
+  // expected-note at +1 {{expected a valid expression}}
+  v = 0--; // expected-error {{expression is not assignable}}
+#pragma omp atomic
+  // expected-error at +2 {{the statement for 'atomic' must be an expression statement of form '++x;', '--x;', 'x++;', 'x--;', 'x binop= expr;', 'x = x binop expr' or 'x = expr binop x', where x is an lvalue expression with scalar type}}
+  // expected-note at +1 {{expected a valid expression}}
+  (void)(0--); // expected-error {{expression is not assignable}}
+#pragma omp atomic
+  // expected-error at +2 {{the statement for 'atomic' must be an expression statement of form '++x;', '--x;', 'x++;', 'x--;', 'x binop= expr;', 'x = x binop expr' or 'x = expr binop x', where x is an lvalue expression with scalar type}}
+  // expected-note at +1 {{expected a valid expression}}
+  x ? 0-- : 1; // expected-error {{expression is not assignable}}
+  const int c = 0; // expected-note {{variable 'c' declared const here}}
+#pragma omp atomic
+  // expected-error at +2 {{the statement for 'atomic' must be an expression statement of form '++x;', '--x;', 'x++;', 'x--;', 'x binop= expr;', 'x = x binop expr' or 'x = expr binop x', where x is an lvalue expression with scalar type}}
+  // expected-note at +1 {{expected a valid expression}}
+  c += 1; // expected-error {{cannot assign to variable 'c' with const-qualified type 'const int'}}
+#pragma omp atomic read
+  0--; // expected-error {{expression is not assignable}}
+#pragma omp atomic write
+  0--; // expected-error {{expression is not assignable}}
+#pragma omp atomic capture
+  { v = x; 0--; } // expected-error {{expression is not assignable}}
+#pragma omp atomic
+  x += 0--; // expected-error {{expression is not assignable}}
+#pragma omp atomic
+  x = x + 0--; // expected-error {{expression is not assignable}}
+#pragma omp atomic
+  (0--)++; // expected-error {{expression is not assignable}}
+#pragma omp atomic capture
+  v = x += 0--; // expected-error {{expression is not assignable}}
+#pragma omp atomic capture
+  { v = x; x += 0--; } // expected-error {{expression is not assignable}}
+#ifdef OMP51
+#pragma omp atomic compare
+  // omp51-error at +2 {{the statement for 'atomic compare' must be a compound statement of form '{x = expr ordop x ? expr : x;}', '{x = x ordop expr? expr : x;}', '{x = x == e ? d : x;}', '{x = e == x ? d : x;}', or 'if(expr ordop x) {x = expr;}', 'if(x ordop expr) {x = expr;}', 'if(x == e) {x = d;}', 'if(e == x) {x = d;}' where 'x' is an lvalue expression with scalar type, 'expr', 'e', and 'd' are expressions with scalar type, and 'ordop' is one of '<' or '>'}}
+  // omp51-note at +1 {{expected assignment statement}}
+  0--; // expected-error {{expression is not assignable}}
+#endif
+  int w = 0--; // expected-error {{expression is not assignable}}
+#pragma omp atomic
+  w++;
+#pragma omp atomic
+  w += 1;
+#pragma omp atomic
+  w = w + 1;
+#pragma omp atomic read
+  v = w;
+#pragma omp atomic write
+  w = 1;
+#pragma omp atomic capture
+  v = w++;
+#pragma omp atomic capture
+  { v = w; w++; }
+#pragma omp atomic capture
+  { w++; v = w; }
+#pragma omp atomic
+  x--;
+}
+
+template <class T> void gh107979_template(T x) {
+#pragma omp atomic
+  // expected-error at +2 {{the statement for 'atomic' must be an expression statement of form '++x;', '--x;', 'x++;', 'x--;', 'x binop= expr;', 'x = x binop expr' or 'x = expr binop x', where x is an lvalue expression with scalar type}}
+  // expected-note at +1 {{expected a valid expression}}
+  0--; // expected-error {{expression is not assignable}}
+#pragma omp atomic
+  x--;
+}
+
 #ifdef OMP51
 struct U {};
 struct U operator<(U, U);


        


More information about the cfe-commits mailing list