Skip to content

Commit a96ce5f

Browse files
authored
[Clang] Fix crash on malformed std::partial_ordering static members (llvm#172001)
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 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 llvm#170015 Fixes llvm#56571
1 parent b10504f commit a96ce5f

File tree

3 files changed

+62
-2
lines changed

3 files changed

+62
-2
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,7 @@ Bug Fixes to C++ Support
608608
- Fixed an issue where templates prevented nested anonymous records from checking the deletion of special members. (#GH167217)
609609
- Fixed spurious diagnoses of certain nested lambda expressions. (#GH149121) (#GH156579)
610610
- Fix the result of ``__is_pointer_interconvertible_base_of`` when arguments are qualified and passed via template parameters. (#GH135273)
611+
- Fixed a crash when standard comparison categories (e.g. ``std::partial_ordering``) are defined with incorrect static member types. (#GH170015) (#GH56571)
611612

612613
Bug Fixes to AST Handling
613614
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/lib/AST/ComparisonCategories.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ bool ComparisonCategoryInfo::ValueInfo::hasValidIntValue() const {
4949
// Before we attempt to get the value of the first field, ensure that we
5050
// actually have one (and only one) field.
5151
const auto *Record = VD->getType()->getAsCXXRecordDecl();
52-
if (Record->getNumFields() != 1 ||
52+
if (!Record || Record->getNumFields() != 1 ||
5353
!Record->field_begin()->getType()->isIntegralOrEnumerationType())
5454
return false;
5555

@@ -83,7 +83,15 @@ ComparisonCategoryInfo::ValueInfo *ComparisonCategoryInfo::lookupValueInfo(
8383
&Ctx.Idents.get(ComparisonCategories::getResultString(ValueKind)));
8484
if (Lookup.empty() || !isa<VarDecl>(Lookup.front()))
8585
return nullptr;
86-
Objects.emplace_back(ValueKind, cast<VarDecl>(Lookup.front()));
86+
// The static member must have the same type as the comparison category class
87+
// itself (e.g., std::partial_ordering::less must be of type
88+
// partial_ordering).
89+
VarDecl *VD = cast<VarDecl>(Lookup.front());
90+
const CXXRecordDecl *VDRecord = VD->getType()->getAsCXXRecordDecl();
91+
if (!VDRecord || VDRecord->getCanonicalDecl() != Record->getCanonicalDecl())
92+
return nullptr;
93+
94+
Objects.emplace_back(ValueKind, VD);
8795
return &Objects.back();
8896
}
8997

clang/test/SemaCXX/PR172001.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// RUN: %clang_cc1 -std=c++20 -verify=test1 -DTEST1 %s
2+
// RUN: %clang_cc1 -std=c++20 -verify=test2 -DTEST2 %s
3+
// RUN: %clang_cc1 -std=c++20 -verify=test3 -DTEST3 %s
4+
// RUN: %clang_cc1 -std=c++20 -verify=test4 -DTEST4 %s
5+
// RUN: %clang_cc1 -std=c++20 -verify=test5 -DTEST5 %s
6+
7+
namespace std {
8+
#ifdef TEST1
9+
// Case 1: Malformed struct (static members are wrong type)
10+
// This triggered the original crash (GH170015).
11+
struct partial_ordering {
12+
static constexpr int equivalent = 0;
13+
static constexpr int less = -1;
14+
static constexpr int greater = 1;
15+
static constexpr int unordered = 2;
16+
};
17+
#elif defined(TEST2)
18+
// Case 2: partial_ordering is a typedef to int
19+
using partial_ordering = int;
20+
#elif defined(TEST3)
21+
// Case 3: partial_ordering is a forward declaration
22+
struct partial_ordering; // test3-note {{forward declaration of 'std::partial_ordering'}}
23+
#elif defined(TEST4)
24+
// Case 4: partial_ordering is a template (Paranoia check)
25+
template <class> struct partial_ordering {
26+
static const partial_ordering less;
27+
static const partial_ordering equivalent;
28+
static const partial_ordering greater;
29+
static const partial_ordering unordered;
30+
};
31+
#elif defined(TEST5)
32+
// Case 5: strong_ordering is a typedef to int (covers GH56571)
33+
using strong_ordering = int;
34+
#endif
35+
}
36+
37+
void f() {
38+
#ifdef TEST5
39+
int a = 0, b = 0; // int <=> int requires std::strong_ordering
40+
#else
41+
float a = 0.0f, b = 0.0f; // float <=> float requires std::partial_ordering
42+
#endif
43+
44+
auto res = a <=> b;
45+
46+
// test1-error@-2 {{standard library implementation of 'std::partial_ordering' is not supported; the type does not have the expected form}}
47+
// test2-error@-3 {{cannot use builtin operator '<=>' because type 'std::partial_ordering' was not found; include <compare>}}
48+
// test3-error@-4 {{incomplete type 'std::partial_ordering' where a complete type is required}}
49+
// test4-error@-5 {{cannot use builtin operator '<=>' because type 'std::partial_ordering' was not found; include <compare>}}
50+
// test5-error@-6 {{cannot use builtin operator '<=>' because type 'std::strong_ordering' was not found; include <compare>}}
51+
}

0 commit comments

Comments
 (0)