Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
11 changes: 9 additions & 2 deletions clang/lib/AST/ComparisonCategories.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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();
}

Expand Down
25 changes: 25 additions & 0 deletions clang/test/SemaCXX/cxx2a-three-way-comparison.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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}}
}
}