[clang] [Clang] Fix crash on malformed std::partial_ordering static members (PR #172001)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Dec 15 23:55:30 PST 2025
https://github.com/code-pankaj updated https://github.com/llvm/llvm-project/pull/172001
>From e627d929cb8bc01dad82610219fbb362635b6f00 Mon Sep 17 00:00:00 2001
From: code-pankaj <pankajyadavpy069 at gmail.com>
Date: Fri, 12 Dec 2025 18:02:34 +0530
Subject: [PATCH 1/6] [Clang] Fix crash on malformed std::partial_ordering
static members
This fixes a crash (segmentation fault) when the standard library implementation of comparison categories (like `std::partial_ordering`) is malformed.
Specifically, if the static members (like `equivalent`) are defined as a primitive type (e.g., `int`) instead of the comparison category type itself, the compiler previously attempted to process them incorrectly, leading to a crash.
This patch adds strict type checking in `ComparisonCategoryInfo::lookupValueInfo`. If the found static member does not match the record type, it is rejected safely, triggering a diagnostic error instead of a crash.
Fixes #170015
---
clang/docs/ReleaseNotes.rst | 1 +
clang/lib/AST/ComparisonCategories.cpp | 11 ++++++--
.../SemaCXX/cxx2a-three-way-comparison.cpp | 25 +++++++++++++++++++
3 files changed, 35 insertions(+), 2 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 22ca79d6adc28..702903bd0225a 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -602,6 +602,7 @@ Bug Fixes to C++ Support
- Fixed an issue where templates prevented nested anonymous records from checking the deletion of special members. (#GH167217)
- Fixed spurious diagnoses of certain nested lambda expressions. (#GH149121) (#GH156579)
- Fix the result of ``__is_pointer_interconvertible_base_of`` when arguments are qualified and passed via template parameters. (#GH135273)
+- - Fixed a crash when standard comparison categories (e.g. ``std::partial_ordering``) are defined with incorrect static member types. (#GH170015)
Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/AST/ComparisonCategories.cpp b/clang/lib/AST/ComparisonCategories.cpp
index 1b9c938e2ace3..afa662dccab00 100644
--- a/clang/lib/AST/ComparisonCategories.cpp
+++ b/clang/lib/AST/ComparisonCategories.cpp
@@ -49,7 +49,7 @@ bool ComparisonCategoryInfo::ValueInfo::hasValidIntValue() const {
// Before we attempt to get the value of the first field, ensure that we
// actually have one (and only one) field.
const auto *Record = VD->getType()->getAsCXXRecordDecl();
- if (Record->getNumFields() != 1 ||
+ if (!Record || Record->getNumFields() != 1 ||
!Record->field_begin()->getType()->isIntegralOrEnumerationType())
return false;
@@ -83,7 +83,14 @@ ComparisonCategoryInfo::ValueInfo *ComparisonCategoryInfo::lookupValueInfo(
&Ctx.Idents.get(ComparisonCategories::getResultString(ValueKind)));
if (Lookup.empty() || !isa<VarDecl>(Lookup.front()))
return nullptr;
- Objects.emplace_back(ValueKind, cast<VarDecl>(Lookup.front()));
+ // The static member must have the same type as the comparison category class
+ // itself (e.g., std::partial_ordering::less must be of type partial_ordering).
+ VarDecl *ValueDecl = cast<VarDecl>(Lookup.front());
+ const CXXRecordDecl *ValueDeclRecord = ValueDecl->getType()->getAsCXXRecordDecl();
+ if (!ValueDeclRecord || ValueDeclRecord->getCanonicalDecl() != Record->getCanonicalDecl())
+ return nullptr;
+
+ Objects.emplace_back(ValueKind, ValueDecl);
return &Objects.back();
}
diff --git a/clang/test/SemaCXX/cxx2a-three-way-comparison.cpp b/clang/test/SemaCXX/cxx2a-three-way-comparison.cpp
index 76007ff3913dd..31b6f367e9cce 100644
--- a/clang/test/SemaCXX/cxx2a-three-way-comparison.cpp
+++ b/clang/test/SemaCXX/cxx2a-three-way-comparison.cpp
@@ -67,3 +67,28 @@ struct comparable_t {
expected-note {{defaulted 'operator<=>' is implicitly deleted because defaulted comparison of vector types is not supported}}
};
} // namespace GH137452
+
+namespace GH170015 {
+// This test ensures that the compiler enforces strict type checking on the
+// static members of comparison category types.
+// Previously, a mismatch (e.g., equivalent being an int) could crash the compiler.
+}
+
+namespace std {
+ struct partial_ordering {
+ // Malformed: 'equivalent' should be of type 'partial_ordering', not 'int'.
+ static constexpr int equivalent = 0;
+ static constexpr int less = -1;
+ static constexpr int greater = 1;
+ static constexpr int unordered = 2;
+ };
+}
+
+namespace GH170015 {
+ void f() {
+ float a = 0.0f, b = 0.0f;
+ // We expect the compiler to complain that the type form is wrong
+ // (because the static members are ints, not objects).
+ auto res = a <=> b; // expected-error {{standard library implementation of 'std::partial_ordering' is not supported; the type does not have the expected form}}
+ }
+}
\ No newline at end of file
>From cd04c2274c377aaf83b272b1d0bc5e8ab4167845 Mon Sep 17 00:00:00 2001
From: code-pankaj <pankajyadavpy069 at gmail.com>
Date: Sat, 13 Dec 2025 14:23:26 +0530
Subject: [PATCH 2/6] [Clang] Update regression tests for GH170015
Moved the regression test to a dedicated file (clang/test/SemaCXX/PR172001.cpp) to cover all requested edge cases, including typedefs and forward declarations. Reverted changes to clang/test/SemaCXX/cxx2a-three-way-comparison.cpp.
---
clang/lib/AST/ComparisonCategories.cpp | 9 ++--
clang/test/SemaCXX/PR172001.cpp | 41 +++++++++++++++++++
.../SemaCXX/cxx2a-three-way-comparison.cpp | 25 -----------
3 files changed, 47 insertions(+), 28 deletions(-)
create mode 100644 clang/test/SemaCXX/PR172001.cpp
diff --git a/clang/lib/AST/ComparisonCategories.cpp b/clang/lib/AST/ComparisonCategories.cpp
index afa662dccab00..c6093c50af7fe 100644
--- a/clang/lib/AST/ComparisonCategories.cpp
+++ b/clang/lib/AST/ComparisonCategories.cpp
@@ -84,10 +84,13 @@ ComparisonCategoryInfo::ValueInfo *ComparisonCategoryInfo::lookupValueInfo(
if (Lookup.empty() || !isa<VarDecl>(Lookup.front()))
return nullptr;
// The static member must have the same type as the comparison category class
- // itself (e.g., std::partial_ordering::less must be of type partial_ordering).
+ // itself (e.g., std::partial_ordering::less must be of type
+ // partial_ordering).
VarDecl *ValueDecl = cast<VarDecl>(Lookup.front());
- const CXXRecordDecl *ValueDeclRecord = ValueDecl->getType()->getAsCXXRecordDecl();
- if (!ValueDeclRecord || ValueDeclRecord->getCanonicalDecl() != Record->getCanonicalDecl())
+ const CXXRecordDecl *ValueDeclRecord =
+ ValueDecl->getType()->getAsCXXRecordDecl();
+ if (!ValueDeclRecord ||
+ ValueDeclRecord->getCanonicalDecl() != Record->getCanonicalDecl())
return nullptr;
Objects.emplace_back(ValueKind, ValueDecl);
diff --git a/clang/test/SemaCXX/PR172001.cpp b/clang/test/SemaCXX/PR172001.cpp
new file mode 100644
index 0000000000000..a493d9114b205
--- /dev/null
+++ b/clang/test/SemaCXX/PR172001.cpp
@@ -0,0 +1,41 @@
+// RUN: %clang_cc1 -std=c++20 -verify=test1 -DTEST1 %s
+// RUN: %clang_cc1 -std=c++20 -verify=test2 -DTEST2 %s
+// RUN: %clang_cc1 -std=c++20 -verify=test3 -DTEST3 %s
+// RUN: %clang_cc1 -std=c++20 -verify=test4 -DTEST4 %s
+
+namespace std {
+#ifdef TEST1
+ // Case 1: Malformed struct (static members are wrong type)
+ // This triggered the original crash (GH170015).
+ struct partial_ordering {
+ static constexpr int equivalent = 0;
+ static constexpr int less = -1;
+ static constexpr int greater = 1;
+ static constexpr int unordered = 2;
+ };
+#elif defined(TEST2)
+ // Case 2: partial_ordering is a typedef to int
+ using partial_ordering = int;
+#elif defined(TEST3)
+ // Case 3: partial_ordering is a forward declaration
+ struct partial_ordering; // test3-note {{forward declaration of 'std::partial_ordering'}}
+#elif defined(TEST4)
+ // Case 4: partial_ordering is a template (Paranoia check)
+ template <class> struct partial_ordering {
+ static const partial_ordering less;
+ static const partial_ordering equivalent;
+ static const partial_ordering greater;
+ static const partial_ordering unordered;
+ };
+#endif
+}
+
+void f() {
+ float a = 0.0f, b = 0.0f;
+ auto res = a <=> b;
+ // test1-error at -1 {{standard library implementation of 'std::partial_ordering' is not supported; the type does not have the expected form}}
+ // test2-error at -2 {{cannot use builtin operator '<=>' because type 'std::partial_ordering' was not found; include <compare>}}
+ // test3-error at -3 {{incomplete type 'std::partial_ordering' where a complete type is required}}
+ // test4-error at -4 {{cannot use builtin operator '<=>' because type 'std::partial_ordering' was not found; include <compare>}}
+}
+
\ No newline at end of file
diff --git a/clang/test/SemaCXX/cxx2a-three-way-comparison.cpp b/clang/test/SemaCXX/cxx2a-three-way-comparison.cpp
index 31b6f367e9cce..76007ff3913dd 100644
--- a/clang/test/SemaCXX/cxx2a-three-way-comparison.cpp
+++ b/clang/test/SemaCXX/cxx2a-three-way-comparison.cpp
@@ -67,28 +67,3 @@ struct comparable_t {
expected-note {{defaulted 'operator<=>' is implicitly deleted because defaulted comparison of vector types is not supported}}
};
} // namespace GH137452
-
-namespace GH170015 {
-// This test ensures that the compiler enforces strict type checking on the
-// static members of comparison category types.
-// Previously, a mismatch (e.g., equivalent being an int) could crash the compiler.
-}
-
-namespace std {
- struct partial_ordering {
- // Malformed: 'equivalent' should be of type 'partial_ordering', not 'int'.
- static constexpr int equivalent = 0;
- static constexpr int less = -1;
- static constexpr int greater = 1;
- static constexpr int unordered = 2;
- };
-}
-
-namespace GH170015 {
- void f() {
- float a = 0.0f, b = 0.0f;
- // We expect the compiler to complain that the type form is wrong
- // (because the static members are ints, not objects).
- auto res = a <=> b; // expected-error {{standard library implementation of 'std::partial_ordering' is not supported; the type does not have the expected form}}
- }
-}
\ No newline at end of file
>From ebc36721bbe13a76ba74fb31aed92cfd00a7d93d Mon Sep 17 00:00:00 2001
From: Pankaj <pankajyadavpy069 at gmail.com>
Date: Sat, 13 Dec 2025 14:36:10 +0530
Subject: [PATCH 3/6] removed extra hyphen
---
clang/docs/ReleaseNotes.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 702903bd0225a..477e6ca67c818 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -602,7 +602,7 @@ Bug Fixes to C++ Support
- Fixed an issue where templates prevented nested anonymous records from checking the deletion of special members. (#GH167217)
- Fixed spurious diagnoses of certain nested lambda expressions. (#GH149121) (#GH156579)
- Fix the result of ``__is_pointer_interconvertible_base_of`` when arguments are qualified and passed via template parameters. (#GH135273)
-- - Fixed a crash when standard comparison categories (e.g. ``std::partial_ordering``) are defined with incorrect static member types. (#GH170015)
+- Fixed a crash when standard comparison categories (e.g. ``std::partial_ordering``) are defined with incorrect static member types. (#GH170015)
Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
>From 49c3c40875344b6cef4e211a9e865328ef460ec0 Mon Sep 17 00:00:00 2001
From: Pankaj <pankajyadavpy069 at gmail.com>
Date: Sat, 13 Dec 2025 18:07:18 +0530
Subject: [PATCH 4/6] added new line
---
clang/test/SemaCXX/PR172001.cpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/clang/test/SemaCXX/PR172001.cpp b/clang/test/SemaCXX/PR172001.cpp
index a493d9114b205..20ac0fe8d8e30 100644
--- a/clang/test/SemaCXX/PR172001.cpp
+++ b/clang/test/SemaCXX/PR172001.cpp
@@ -38,4 +38,6 @@ void f() {
// test3-error at -3 {{incomplete type 'std::partial_ordering' where a complete type is required}}
// test4-error at -4 {{cannot use builtin operator '<=>' because type 'std::partial_ordering' was not found; include <compare>}}
}
+
+
\ No newline at end of file
>From ed2e848d2bdbd42f799443b25874fa14ccf454a0 Mon Sep 17 00:00:00 2001
From: Pankaj <pankajyadavpy069 at gmail.com>
Date: Sat, 13 Dec 2025 18:14:44 +0530
Subject: [PATCH 5/6] new line fix
---
clang/test/SemaCXX/PR172001.cpp | 2 --
1 file changed, 2 deletions(-)
diff --git a/clang/test/SemaCXX/PR172001.cpp b/clang/test/SemaCXX/PR172001.cpp
index 20ac0fe8d8e30..ba5f8e68707e9 100644
--- a/clang/test/SemaCXX/PR172001.cpp
+++ b/clang/test/SemaCXX/PR172001.cpp
@@ -39,5 +39,3 @@ void f() {
// test4-error at -4 {{cannot use builtin operator '<=>' because type 'std::partial_ordering' was not found; include <compare>}}
}
-
-
\ No newline at end of file
>From 309dad480029f69653d260d9a183c5ec5016f854 Mon Sep 17 00:00:00 2001
From: code-pankaj <pankajyadavpy069 at gmail.com>
Date: Tue, 16 Dec 2025 13:23:10 +0530
Subject: [PATCH 6/6] renamed ValueDecl to VD and ValueDeclRecord to VDRecord
---
clang/lib/AST/ComparisonCategories.cpp | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/clang/lib/AST/ComparisonCategories.cpp b/clang/lib/AST/ComparisonCategories.cpp
index c6093c50af7fe..355123f6a90ec 100644
--- a/clang/lib/AST/ComparisonCategories.cpp
+++ b/clang/lib/AST/ComparisonCategories.cpp
@@ -86,14 +86,14 @@ ComparisonCategoryInfo::ValueInfo *ComparisonCategoryInfo::lookupValueInfo(
// The static member must have the same type as the comparison category class
// itself (e.g., std::partial_ordering::less must be of type
// partial_ordering).
- VarDecl *ValueDecl = cast<VarDecl>(Lookup.front());
- const CXXRecordDecl *ValueDeclRecord =
- ValueDecl->getType()->getAsCXXRecordDecl();
- if (!ValueDeclRecord ||
- ValueDeclRecord->getCanonicalDecl() != Record->getCanonicalDecl())
+ VarDecl *VD = cast<VarDecl>(Lookup.front());
+ const CXXRecordDecl *VDRecord =
+ VD->getType()->getAsCXXRecordDecl();
+ if (!VDRecord ||
+ VDRecord->getCanonicalDecl() != Record->getCanonicalDecl())
return nullptr;
- Objects.emplace_back(ValueKind, ValueDecl);
+ Objects.emplace_back(ValueKind, VD);
return &Objects.back();
}
More information about the cfe-commits
mailing list