https://github.com/yronglin created 
https://github.com/llvm/llvm-project/pull/202696

The crash is from an internal inconsistency in Clang’s expression 
classification.

Expr::ClassifyImpl computes a classification like CL_LValue or CL_PRValue, then 
asserts that this agrees with the AST node’s own value category:

- clang/lib/AST/ExprClassification.cpp:37
- CL_LValue must satisfy E->isLValue()
- CL_PRValue must satisfy E->isPRValue()

Fixes https://github.com/llvm/llvm-project/issues/202693.

>From 4eff537771629643c6fe48b858826b57f4ab368a Mon Sep 17 00:00:00 2001
From: yronglin <[email protected]>
Date: Tue, 9 Jun 2026 23:47:34 +0800
Subject: [PATCH] [clang] Classify binary op value kinds use
 ClassifyExprValueKind when it's type-dependent

Signed-off-by: yronglin <[email protected]>
---
 clang/docs/ReleaseNotes.rst                   |  2 +
 clang/lib/AST/ExprClassification.cpp          |  9 +++--
 .../dependent-assignment-classification.cpp   | 39 +++++++++++++++++++
 3 files changed, 46 insertions(+), 4 deletions(-)
 create mode 100644 clang/test/AST/dependent-assignment-classification.cpp

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 8a00b235860e4..340c0285072f9 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -744,6 +744,8 @@ Bug Fixes to C++ Support
 - We no longer consider conversion operators when copy-initializing from the 
same type. This was non
   conforming and could lead to recursive constraint satisfaction checking. 
(#GH149443)
 - Fixed a crash in Itanium C++ name mangling for a lambda in a local class 
field initializer inside a constructor/destructor. (#GH176395)
+- Fixed a crash when Expr::ClassifyImpl computes a classification like 
CL_LValue or CL_PRValue, then asserts that this 
+  agrees with the AST node's own value category. (#GH202693)
 - Fixed crashes in Itanium C++ name mangling for lambdas with trailing 
requires-clauses involving requires-expressions. (#GH100774) (#GH123854)
 - Fixed an invalid rejection and assertion failure while generating 
``operator=`` for fields with the ``__restrict`` qualifier. (#GH37979)
 - Fixed a use-after-free bug when parsing default arguments containing lambdas 
in declarations with template-id declarators. (#GH196725)
diff --git a/clang/lib/AST/ExprClassification.cpp 
b/clang/lib/AST/ExprClassification.cpp
index a83c17074ea69..d153afda71c4b 100644
--- a/clang/lib/AST/ExprClassification.cpp
+++ b/clang/lib/AST/ExprClassification.cpp
@@ -617,11 +617,12 @@ static Cl::Kinds ClassifyBinaryOp(ASTContext &Ctx, const 
BinaryOperator *E) {
   assert(Ctx.getLangOpts().CPlusPlus &&
          "This is only relevant for C++.");
 
-  // For binary operators which are unknown due to type dependence, the
-  // convention is to classify them as a prvalue. This does not matter much, 
but
-  // it needs to agree with how they are created.
+  // For binary operators which are unknown due to type dependence, use the
+  // value kind assigned when the expression was created. Dependent assignment
+  // expressions can be either lvalues or prvalues depending on whether they
+  // might resolve to an overloaded operator.
   if (E->getType() == Ctx.DependentTy)
-    return Cl::CL_PRValue;
+    return ClassifyExprValueKind(Ctx.getLangOpts(), E, E->getValueKind());
 
   // C++ [expr.ass]p1: All [...] return an lvalue referring to the left 
operand.
   // Except we override this for writes to ObjC properties.
diff --git a/clang/test/AST/dependent-assignment-classification.cpp 
b/clang/test/AST/dependent-assignment-classification.cpp
new file mode 100644
index 0000000000000..d31fa07fe0116
--- /dev/null
+++ b/clang/test/AST/dependent-assignment-classification.cpp
@@ -0,0 +1,39 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++26 %s
+
+// expected-no-diagnostics
+
+template <auto X, class = decltype(X)>
+struct constant_wrapper;
+
+template <class T>
+concept constexpr_param = requires { typename constant_wrapper<T::value>; };
+
+struct ops {
+  template <constexpr_param T, constexpr_param R>
+  constexpr auto operator+=(this T, R)
+      -> constant_wrapper<(T::value += R::value)> {
+    return {};
+  }
+};
+
+template <auto X, class>
+struct constant_wrapper : ops {
+  static constexpr decltype(auto) value = (X);
+
+  template <constexpr_param R>
+  constexpr auto operator=(R) const -> constant_wrapper<(value = R::value)> {
+    return {};
+  }
+};
+
+struct A {
+  int n;
+  constexpr A(int n) : n(n) {}
+  constexpr A operator=(A rhs) const { return A{rhs.n}; }
+  constexpr A operator+=(A rhs) const { return A{n + rhs.n}; }
+};
+
+using X = constant_wrapper<A{1}>;
+using Y = constant_wrapper<A{2}>;
+using SimpleAssignment = decltype(X{} = Y{});
+using CompoundAssignment = decltype(X{} += Y{});

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to