[clang] [Clang] Fix CXXRewrittenBinaryOperator::getDecomposedForm to handle case when spaceship operator returns comparison category by reference (PR #66270)

Shafik Yaghmour via cfe-commits cfe-commits at lists.llvm.org
Mon Sep 25 12:55:50 PDT 2023


https://github.com/shafik updated https://github.com/llvm/llvm-project/pull/66270

>From b934841d7fc3d3447b23a9718a38742943f76916 Mon Sep 17 00:00:00 2001
From: Shafik Yaghmour <shafik at users.noreply.github.com>
Date: Wed, 13 Sep 2023 11:09:28 -0700
Subject: [PATCH 1/3] [Clang] Fix CXXRewrittenBinaryOperator::getDecomposedForm
 to handle case when spaceship operator returns comparison category by
 reference

Currently CXXRewrittenBinaryOperator::getDecomposedForm(...) may crash if the
spaceship operator returns a comparison category by reference. This because
IgnoreImplicitAsWritten() does not look through CXXConstructExpr. The fix is to
use IgnoreUnlessSpelledInSource() which will look though CXXConstructExpr.

This fixes: https://github.com/llvm/llvm-project/issues/64162
---
 clang/lib/AST/ExprCXX.cpp            |  2 +-
 clang/test/SemaCXX/compare-cxx2a.cpp | 23 +++++++++++++++++++++++
 2 files changed, 24 insertions(+), 1 deletion(-)

diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index efb64842fb6d96d..06163255f9b5e54 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -111,7 +111,7 @@ CXXRewrittenBinaryOperator::getDecomposedForm() const {
     return Result;
 
   // Otherwise, we expect a <=> to now be on the LHS.
-  E = Result.LHS->IgnoreImplicitAsWritten();
+  E = Result.LHS->IgnoreUnlessSpelledInSource();
   if (auto *BO = dyn_cast<BinaryOperator>(E)) {
     assert(BO->getOpcode() == BO_Cmp);
     Result.LHS = BO->getLHS();
diff --git a/clang/test/SemaCXX/compare-cxx2a.cpp b/clang/test/SemaCXX/compare-cxx2a.cpp
index 619e16aa7458179..15a0baccfca17a2 100644
--- a/clang/test/SemaCXX/compare-cxx2a.cpp
+++ b/clang/test/SemaCXX/compare-cxx2a.cpp
@@ -479,3 +479,26 @@ void DoSomething() {
                                                   // expected-note  {{undefined function 'operator++' cannot be used in a constant expression}}
 }
 }
+
+namespace GH64162 {
+struct S {
+    const std::strong_ordering& operator<=>(const S&) const = default;
+};
+bool test(S s) {
+    return s < s; // We expect this not to crash anymore
+}
+
+// Following example never crashed but worth adding in because it is related
+struct A {};
+bool operator<(A, int);
+
+struct B {
+    operator A();
+};
+
+struct C {
+    B operator<=>(C);
+};
+
+bool f(C c) { return c < c; }
+}

>From f91332e6e51fe442ecec2db2d203aa7650d9d7ab Mon Sep 17 00:00:00 2001
From: Shafik Yaghmour <shafik at users.noreply.github.com>
Date: Wed, 13 Sep 2023 11:09:28 -0700
Subject: [PATCH 2/3] [Clang] Fix CXXRewrittenBinaryOperator::getDecomposedForm
 to handle case when spaceship operator returns comparison category by
 reference

Currently CXXRewrittenBinaryOperator::getDecomposedForm(...) may crash if the
spaceship operator returns a comparison category by reference. This because
IgnoreImplicitAsWritten() does not look through CXXConstructExpr. The fix is to
use IgnoreUnlessSpelledInSource() which will look though CXXConstructExpr.

This fixes: https://github.com/llvm/llvm-project/issues/64162
---
 clang/docs/ReleaseNotes.rst          |  4 ++++
 clang/lib/AST/ExprCXX.cpp            |  2 +-
 clang/test/SemaCXX/compare-cxx2a.cpp | 23 +++++++++++++++++++++++
 3 files changed, 28 insertions(+), 1 deletion(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 3cdad2f7b9f0e5a..e35e7ed941bc79c 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -281,6 +281,10 @@ Bug Fixes to C++ Support
   a non-template inner-class between the function and the class template.
   (`#65810 <https://github.com/llvm/llvm-project/issues/65810>`_)
 
+- Fix crash caused by a spaceship operator returning a comparision category by
+  reference. Fixes:
+  (`#64162 <https://github.com/llvm/llvm-project/issues/64162>`_)
+
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^
 - Fixed an import failure of recursive friend class template.
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index efb64842fb6d96d..06163255f9b5e54 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -111,7 +111,7 @@ CXXRewrittenBinaryOperator::getDecomposedForm() const {
     return Result;
 
   // Otherwise, we expect a <=> to now be on the LHS.
-  E = Result.LHS->IgnoreImplicitAsWritten();
+  E = Result.LHS->IgnoreUnlessSpelledInSource();
   if (auto *BO = dyn_cast<BinaryOperator>(E)) {
     assert(BO->getOpcode() == BO_Cmp);
     Result.LHS = BO->getLHS();
diff --git a/clang/test/SemaCXX/compare-cxx2a.cpp b/clang/test/SemaCXX/compare-cxx2a.cpp
index 619e16aa7458179..15a0baccfca17a2 100644
--- a/clang/test/SemaCXX/compare-cxx2a.cpp
+++ b/clang/test/SemaCXX/compare-cxx2a.cpp
@@ -479,3 +479,26 @@ void DoSomething() {
                                                   // expected-note  {{undefined function 'operator++' cannot be used in a constant expression}}
 }
 }
+
+namespace GH64162 {
+struct S {
+    const std::strong_ordering& operator<=>(const S&) const = default;
+};
+bool test(S s) {
+    return s < s; // We expect this not to crash anymore
+}
+
+// Following example never crashed but worth adding in because it is related
+struct A {};
+bool operator<(A, int);
+
+struct B {
+    operator A();
+};
+
+struct C {
+    B operator<=>(C);
+};
+
+bool f(C c) { return c < c; }
+}

>From 0193c9f98fad8941691d92306c423184074f52e9 Mon Sep 17 00:00:00 2001
From: Shafik Yaghmour <shafik at users.noreply.github.com>
Date: Wed, 13 Sep 2023 11:09:28 -0700
Subject: [PATCH 3/3] [Clang] Fix CXXRewrittenBinaryOperator::getDecomposedForm
 to handle case when spaceship operator returns comparison category by
 reference

Currently CXXRewrittenBinaryOperator::getDecomposedForm(...) may crash if the
spaceship operator returns a comparison category by reference. This because
IgnoreImplicitAsWritten() does not look through CXXConstructExpr. The fix is to
use IgnoreUnlessSpelledInSource() which will look though CXXConstructExpr.

This fixes: https://github.com/llvm/llvm-project/issues/64162
---
 clang/docs/ReleaseNotes.rst          |  4 ++++
 clang/lib/AST/ExprCXX.cpp            |  2 +-
 clang/test/SemaCXX/compare-cxx2a.cpp | 23 +++++++++++++++++++++++
 3 files changed, 28 insertions(+), 1 deletion(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 294b77a3e260908..688454d6b562ec3 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -353,6 +353,10 @@ Bug Fixes to C++ Support
   NTTP types are compared with the 'diff' method.
   (`#66744 https://github.com/llvm/llvm-project/issues/66744`)
 
+- Fix crash caused by a spaceship operator returning a comparision category by
+  reference. Fixes:
+  (`#64162 <https://github.com/llvm/llvm-project/issues/64162>`_)
+
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^
 - Fixed an import failure of recursive friend class template.
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index efb64842fb6d96d..06163255f9b5e54 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -111,7 +111,7 @@ CXXRewrittenBinaryOperator::getDecomposedForm() const {
     return Result;
 
   // Otherwise, we expect a <=> to now be on the LHS.
-  E = Result.LHS->IgnoreImplicitAsWritten();
+  E = Result.LHS->IgnoreUnlessSpelledInSource();
   if (auto *BO = dyn_cast<BinaryOperator>(E)) {
     assert(BO->getOpcode() == BO_Cmp);
     Result.LHS = BO->getLHS();
diff --git a/clang/test/SemaCXX/compare-cxx2a.cpp b/clang/test/SemaCXX/compare-cxx2a.cpp
index 619e16aa7458179..15a0baccfca17a2 100644
--- a/clang/test/SemaCXX/compare-cxx2a.cpp
+++ b/clang/test/SemaCXX/compare-cxx2a.cpp
@@ -479,3 +479,26 @@ void DoSomething() {
                                                   // expected-note  {{undefined function 'operator++' cannot be used in a constant expression}}
 }
 }
+
+namespace GH64162 {
+struct S {
+    const std::strong_ordering& operator<=>(const S&) const = default;
+};
+bool test(S s) {
+    return s < s; // We expect this not to crash anymore
+}
+
+// Following example never crashed but worth adding in because it is related
+struct A {};
+bool operator<(A, int);
+
+struct B {
+    operator A();
+};
+
+struct C {
+    B operator<=>(C);
+};
+
+bool f(C c) { return c < c; }
+}



More information about the cfe-commits mailing list