[clang] [clang][SemaCXX] Fix crash when using static_cast to _Atomic types in C++ (PR #207616)

via cfe-commits cfe-commits at lists.llvm.org
Sun Jul 5 14:57:08 PDT 2026


https://github.com/AZero13 updated https://github.com/llvm/llvm-project/pull/207616

>From 37f880fb72545ee0428ade7c0f1db74e0dbc6feb Mon Sep 17 00:00:00 2001
From: AZero13 <gfunni234 at gmail.com>
Date: Sun, 5 Jul 2026 17:50:56 -0400
Subject: [PATCH] [clang][SemaCXX] Fix crash when using static_cast to _Atomic
 types in C++

This PR fixes a compiler crash (assertion failure) in both the constant evaluator (`ExprConstant.cpp`) and LLVM CodeGen (`CGExprAgg.cpp`) when evaluating a `static_cast` to an `_Atomic` type in C++ (e.g. `static_cast<_Atomic(struct S)>(S{})`).

When evaluating casts, `CastOperation::CastOperation` in `SemaCast.cpp` was unconditionally stripping the `_Atomic` qualifier from the destination type using `getAtomicUnqualifiedType()`. This behavior is correct for C23 (under C23 6.5.4p6, where cast expressions yield the non-atomic version of the type), but it is incorrect for C++ where `_Atomic(T)` is treated as a distinct type wrapper.

Because `_Atomic` was stripped from the destination type in C++, the cast evaluation mechanism built the AST conversion targeting the underlying type `T` (resulting in a `CK_ConstructorConversion` for class types).

However, the resulting `CXXStaticCastExpr` node was still created with the original type `_Atomic(T)`. This type mismatch (an atomic node wrapping a constructor conversion targeting a non-atomic struct) triggered assertions during both constant evaluation and code generation.

Modify `CastOperation::CastOperation` to only strip the `_Atomic` qualifier in C mode. In C++ mode, we preserve the `_Atomic` wrapper, ensuring that `InitializationSequence` properly evaluates the target type as `_Atomic(T)` and adds the implicit `CK_NonAtomicToAtomic` conversion step.

Fixes #207610.
---
 clang/lib/Sema/SemaCast.cpp        | 8 +++++++-
 clang/test/SemaCXX/atomic-type.cpp | 9 +++++++++
 2 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp
index 133837623a7a6..0d0b20a4395f2 100644
--- a/clang/lib/Sema/SemaCast.cpp
+++ b/clang/lib/Sema/SemaCast.cpp
@@ -78,7 +78,13 @@ namespace {
       // the qualifier.
       if (!S.Context.getLangOpts().ObjC && !DestType->isRecordType() &&
           !DestType->isArrayType() && !DestType.getPointerAuth()) {
-        DestType = DestType.getAtomicUnqualifiedType();
+        if (S.Context.getLangOpts().CPlusPlus) {
+          // Note that in C++, _Atomic(T) is a distinct type, not a
+          // cv-qualifier, so it is not stripped.
+          DestType = DestType.getUnqualifiedType();
+        } else {
+          DestType = DestType.getAtomicUnqualifiedType();
+        }
       }
 
       if (const BuiltinType *placeholder =
diff --git a/clang/test/SemaCXX/atomic-type.cpp b/clang/test/SemaCXX/atomic-type.cpp
index 3200a59bc86a2..b8f8f184da643 100644
--- a/clang/test/SemaCXX/atomic-type.cpp
+++ b/clang/test/SemaCXX/atomic-type.cpp
@@ -111,3 +111,12 @@ namespace non_trivially_copyable {
   _Atomic S s;  // expected-error {{_Atomic cannot be applied to type 'S' which is not trivially copyable}} \
                 // expected-warning {{'_Atomic' is a C11 extension}}
 }
+
+namespace static_cast_crash {
+  struct S { char a; };
+  void static_cast_non_atomic_to_atomic() {
+    _Atomic struct S a; // expected-warning {{'_Atomic' is a C11 extension}}
+    a = static_cast<_Atomic(struct S)>(S()); // expected-warning {{'_Atomic' is a C11 extension}}
+  }
+}
+



More information about the cfe-commits mailing list