Skip to content

Commit b1a8089

Browse files
authored
[Clang] Introduce __builtin_meow_synthesises_from_spaceship (#155612)
This set of builtins makes it possible to detect whether a comparison operation is synthesised from a spaceship operator. This makes it possible to avoid calling the comparison multiple times if you care about the three-way relation. This is especially interesting for the associative containers from the STL, since a lot of functions call the comparator twice to establish the relation. With this builtin these functions can call the comparator just once and use the result of the three way comparison directly.
1 parent bf4486e commit b1a8089

File tree

5 files changed

+272
-3
lines changed

5 files changed

+272
-3
lines changed

clang/docs/LanguageExtensions.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2049,6 +2049,9 @@ The following type trait primitives are supported by Clang. Those traits marked
20492049
Returns true if a reference ``T`` can be copy-initialized from a temporary of type
20502050
a non-cv-qualified ``U``.
20512051
* ``__underlying_type`` (C++, GNU, Microsoft)
2052+
* ``__builtin_lt_synthesises_from_spaceship``, ``__builtin_gt_synthesises_from_spaceship``,
2053+
``__builtin_le_synthesises_from_spaceship``, ``__builtin_ge_synthesises_from_spaceship`` (Clang):
2054+
These builtins can be used to determine whether the corresponding operator is synthesised from a spaceship operator.
20522055

20532056
In addition, the following expression traits are supported:
20542057

clang/docs/ReleaseNotes.rst

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,11 @@ What's New in Clang |release|?
109109
C++ Language Changes
110110
--------------------
111111

112+
- A new family of builtins ``__builtin_*_synthesises_from_spaceship`` has been added. These can be queried to know
113+
whether the ``<`` (``lt``), ``>`` (``gt``), ``<=`` (``le``), or ``>=`` (``ge``) operators are synthesised from a
114+
``<=>``. This makes it possible to optimize certain facilities by using the ``<=>`` operation directly instead of
115+
doing multiple comparisons.
116+
112117
C++2c Feature Support
113118
^^^^^^^^^^^^^^^^^^^^^
114119

@@ -241,7 +246,7 @@ Improvements to Clang's diagnostics
241246
"format specifies type 'unsigned int' but the argument has type 'int', which differs in signedness [-Wformat-signedness]"
242247
"signedness of format specifier 'u' is incompatible with 'c' [-Wformat-signedness]"
243248
and the API-visible diagnostic id will be appropriate.
244-
249+
245250
- Fixed false positives in ``-Waddress-of-packed-member`` diagnostics when
246251
potential misaligned members get processed before they can get discarded.
247252
(#GH144729)
@@ -275,7 +280,7 @@ Bug Fixes in This Version
275280
-------------------------
276281
- Fix a crash when marco name is empty in ``#pragma push_macro("")`` or
277282
``#pragma pop_macro("")``. (#GH149762).
278-
- Fix a crash in variable length array (e.g. ``int a[*]``) function parameter type
283+
- Fix a crash in variable length array (e.g. ``int a[*]``) function parameter type
279284
being used in ``_Countof`` expression. (#GH152826).
280285
- ``-Wunreachable-code`` now diagnoses tautological or contradictory
281286
comparisons such as ``x != 0 || x != 1.0`` and ``x == 0 && x == 1.0`` on
@@ -360,7 +365,7 @@ X86 Support
360365
arithmetic can now be used in C++ constant expressions.
361366
- Some SSE, AVX and AVX512 intrinsics have been converted to wrap
362367
generic __builtin intrinsics.
363-
- NOTE: Please avoid use of the __builtin_ia32_* intrinsics - these are not
368+
- NOTE: Please avoid use of the __builtin_ia32_* intrinsics - these are not
364369
guaranteed to exist in future releases, or match behaviour with previous
365370
releases of clang or other compilers.
366371

clang/include/clang/Basic/TokenKinds.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,10 @@ TYPE_TRAIT_1(__can_pass_in_regs, CanPassInRegs, KEYCXX)
552552
TYPE_TRAIT_2(__reference_binds_to_temporary, ReferenceBindsToTemporary, KEYCXX)
553553
TYPE_TRAIT_2(__reference_constructs_from_temporary, ReferenceConstructsFromTemporary, KEYCXX)
554554
TYPE_TRAIT_2(__reference_converts_from_temporary, ReferenceConvertsFromTemporary, KEYCXX)
555+
TYPE_TRAIT_2(__builtin_lt_synthesises_from_spaceship, LtSynthesisesFromSpaceship, KEYCXX)
556+
TYPE_TRAIT_2(__builtin_le_synthesises_from_spaceship, LeSynthesisesFromSpaceship, KEYCXX)
557+
TYPE_TRAIT_2(__builtin_gt_synthesises_from_spaceship, GtSynthesisesFromSpaceship, KEYCXX)
558+
TYPE_TRAIT_2(__builtin_ge_synthesises_from_spaceship, GeSynthesisesFromSpaceship, KEYCXX)
555559
// IsDeducible is only used internally by clang for CTAD implementation and
556560
// is not exposed to users.
557561
TYPE_TRAIT_2(/*EmptySpellingName*/, IsDeducible, KEYCXX)

clang/lib/Sema/SemaTypeTraits.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1825,6 +1825,51 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT,
18251825

18261826
return Self.HLSL().IsScalarizedLayoutCompatible(LhsT, RhsT);
18271827
}
1828+
case BTT_LtSynthesisesFromSpaceship:
1829+
case BTT_LeSynthesisesFromSpaceship:
1830+
case BTT_GtSynthesisesFromSpaceship:
1831+
case BTT_GeSynthesisesFromSpaceship: {
1832+
EnterExpressionEvaluationContext UnevaluatedContext(
1833+
Self, Sema::ExpressionEvaluationContext::Unevaluated);
1834+
Sema::SFINAETrap SFINAE(Self, /*ForValidityCheck=*/true);
1835+
Sema::ContextRAII TUContext(Self, Self.Context.getTranslationUnitDecl());
1836+
1837+
OpaqueValueExpr LHS(KeyLoc, LhsT.getNonReferenceType(),
1838+
LhsT->isLValueReferenceType() ? ExprValueKind::VK_LValue
1839+
: LhsT->isRValueReferenceType()
1840+
? ExprValueKind::VK_XValue
1841+
: ExprValueKind::VK_PRValue);
1842+
OpaqueValueExpr RHS(KeyLoc, RhsT.getNonReferenceType(),
1843+
RhsT->isLValueReferenceType() ? ExprValueKind::VK_LValue
1844+
: RhsT->isRValueReferenceType()
1845+
? ExprValueKind::VK_XValue
1846+
: ExprValueKind::VK_PRValue);
1847+
1848+
auto OpKind = [&] {
1849+
switch (BTT) {
1850+
case BTT_LtSynthesisesFromSpaceship:
1851+
return BinaryOperatorKind::BO_LT;
1852+
case BTT_LeSynthesisesFromSpaceship:
1853+
return BinaryOperatorKind::BO_LE;
1854+
case BTT_GtSynthesisesFromSpaceship:
1855+
return BinaryOperatorKind::BO_GT;
1856+
case BTT_GeSynthesisesFromSpaceship:
1857+
return BinaryOperatorKind::BO_GE;
1858+
default:
1859+
llvm_unreachable("Trying to Synthesize non-comparison operator?");
1860+
}
1861+
}();
1862+
1863+
UnresolvedSet<16> Functions;
1864+
Self.LookupBinOp(Self.TUScope, KeyLoc, OpKind, Functions);
1865+
1866+
ExprResult Result =
1867+
Self.CreateOverloadedBinOp(KeyLoc, OpKind, Functions, &LHS, &RHS);
1868+
if (Result.isInvalid() || SFINAE.hasErrorOccurred())
1869+
return false;
1870+
1871+
return isa<CXXRewrittenBinaryOperator>(Result.get());
1872+
}
18281873
default:
18291874
llvm_unreachable("not a BTT");
18301875
}
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s
2+
3+
static_assert(!__builtin_lt_synthesises_from_spaceship()); // expected-error {{expected a type}}
4+
static_assert(!__builtin_lt_synthesises_from_spaceship(int)); // expected-error {{type trait requires 2 arguments; have 1 argument}}
5+
static_assert(!__builtin_lt_synthesises_from_spaceship(int, int, int)); // expected-error {{type trait requires 2 arguments; have 3 argument}}
6+
static_assert(!__builtin_lt_synthesises_from_spaceship(int, 0)); // expected-error {{expected a type}}
7+
8+
static_assert(!__builtin_le_synthesises_from_spaceship()); // expected-error {{expected a type}}
9+
static_assert(!__builtin_le_synthesises_from_spaceship(int)); // expected-error {{type trait requires 2 arguments; have 1 argument}}
10+
static_assert(!__builtin_le_synthesises_from_spaceship(int, int, int)); // expected-error {{type trait requires 2 arguments; have 3 argument}}
11+
static_assert(!__builtin_le_synthesises_from_spaceship(int, 0)); // expected-error {{expected a type}}
12+
13+
static_assert(!__builtin_gt_synthesises_from_spaceship()); // expected-error {{expected a type}}
14+
static_assert(!__builtin_gt_synthesises_from_spaceship(int)); // expected-error {{type trait requires 2 arguments; have 1 argument}}
15+
static_assert(!__builtin_gt_synthesises_from_spaceship(int, int, int)); // expected-error {{type trait requires 2 arguments; have 3 argument}}
16+
static_assert(!__builtin_gt_synthesises_from_spaceship(int, 0)); // expected-error {{expected a type}}
17+
18+
static_assert(!__builtin_ge_synthesises_from_spaceship()); // expected-error {{expected a type}}
19+
static_assert(!__builtin_ge_synthesises_from_spaceship(int)); // expected-error {{type trait requires 2 arguments; have 1 argument}}
20+
static_assert(!__builtin_ge_synthesises_from_spaceship(int, int, int)); // expected-error {{type trait requires 2 arguments; have 3 argument}}
21+
static_assert(!__builtin_ge_synthesises_from_spaceship(int, 0)); // expected-error {{expected a type}}
22+
23+
namespace std {
24+
struct strong_ordering {
25+
int n;
26+
constexpr operator int() const { return n; }
27+
static const strong_ordering less, equal, greater;
28+
};
29+
constexpr strong_ordering strong_ordering::less = {-1};
30+
constexpr strong_ordering strong_ordering::equal = {0};
31+
constexpr strong_ordering strong_ordering::greater = {1};
32+
}
33+
34+
struct DefaultSpaceship {
35+
friend auto operator<=>(DefaultSpaceship, DefaultSpaceship) = default;
36+
};
37+
38+
static_assert(__builtin_lt_synthesises_from_spaceship(const DefaultSpaceship&, const DefaultSpaceship&));
39+
static_assert(__builtin_le_synthesises_from_spaceship(const DefaultSpaceship&, const DefaultSpaceship&));
40+
static_assert(__builtin_gt_synthesises_from_spaceship(const DefaultSpaceship&, const DefaultSpaceship&));
41+
static_assert(__builtin_ge_synthesises_from_spaceship(const DefaultSpaceship&, const DefaultSpaceship&));
42+
43+
struct CustomSpaceship {
44+
int i;
45+
46+
friend auto operator<=>(CustomSpaceship lhs, CustomSpaceship rhs) {
47+
return rhs.i <=> lhs.i;
48+
}
49+
};
50+
51+
static_assert(__builtin_lt_synthesises_from_spaceship(const CustomSpaceship&, const CustomSpaceship&));
52+
static_assert(__builtin_le_synthesises_from_spaceship(const CustomSpaceship&, const CustomSpaceship&));
53+
static_assert(__builtin_gt_synthesises_from_spaceship(const CustomSpaceship&, const CustomSpaceship&));
54+
static_assert(__builtin_ge_synthesises_from_spaceship(const CustomSpaceship&, const CustomSpaceship&));
55+
56+
struct CustomLT {
57+
int i;
58+
59+
friend auto operator<(CustomLT lhs, CustomLT rhs) {
60+
return rhs.i < lhs.i;
61+
}
62+
};
63+
64+
static_assert(!__builtin_lt_synthesises_from_spaceship(const CustomLT&, const CustomLT&));
65+
static_assert(!__builtin_le_synthesises_from_spaceship(const CustomLT&, const CustomLT&));
66+
static_assert(!__builtin_gt_synthesises_from_spaceship(const CustomLT&, const CustomLT&));
67+
static_assert(!__builtin_ge_synthesises_from_spaceship(const CustomLT&, const CustomLT&));
68+
69+
struct CustomLE {
70+
int i;
71+
72+
friend auto operator<=(CustomLE lhs, CustomLE rhs) {
73+
return rhs.i < lhs.i;
74+
}
75+
};
76+
77+
static_assert(!__builtin_lt_synthesises_from_spaceship(const CustomLE&, const CustomLE&));
78+
static_assert(!__builtin_le_synthesises_from_spaceship(const CustomLE&, const CustomLE&));
79+
static_assert(!__builtin_gt_synthesises_from_spaceship(const CustomLE&, const CustomLE&));
80+
static_assert(!__builtin_ge_synthesises_from_spaceship(const CustomLE&, const CustomLE&));
81+
82+
struct CustomGT {
83+
int i;
84+
85+
friend auto operator>(CustomGT lhs, CustomGT rhs) {
86+
return rhs.i < lhs.i;
87+
}
88+
};
89+
90+
static_assert(!__builtin_lt_synthesises_from_spaceship(const CustomGT&, const CustomGT&));
91+
static_assert(!__builtin_le_synthesises_from_spaceship(const CustomGT&, const CustomGT&));
92+
static_assert(!__builtin_gt_synthesises_from_spaceship(const CustomGT&, const CustomGT&));
93+
static_assert(!__builtin_ge_synthesises_from_spaceship(const CustomGT&, const CustomGT&));
94+
95+
struct CustomGE {
96+
int i;
97+
98+
friend auto operator>=(CustomGE lhs, CustomGE rhs) {
99+
return rhs.i < lhs.i;
100+
}
101+
};
102+
103+
static_assert(!__builtin_lt_synthesises_from_spaceship(const CustomGE&, const CustomGE&));
104+
static_assert(!__builtin_le_synthesises_from_spaceship(const CustomGE&, const CustomGE&));
105+
static_assert(!__builtin_gt_synthesises_from_spaceship(const CustomGE&, const CustomGE&));
106+
static_assert(!__builtin_ge_synthesises_from_spaceship(const CustomGE&, const CustomGE&));
107+
108+
struct CustomLTAndSpaceship {
109+
int i;
110+
111+
friend auto operator<=>(CustomLTAndSpaceship lhs, CustomLTAndSpaceship rhs) {
112+
return rhs.i <=> lhs.i;
113+
}
114+
115+
friend auto operator<(CustomLTAndSpaceship lhs, CustomLTAndSpaceship rhs) {
116+
return rhs.i < lhs.i;
117+
}
118+
};
119+
120+
static_assert(!__builtin_lt_synthesises_from_spaceship(const CustomLTAndSpaceship&, const CustomLTAndSpaceship&));
121+
static_assert(__builtin_le_synthesises_from_spaceship(const CustomLTAndSpaceship&, const CustomLTAndSpaceship&));
122+
static_assert(__builtin_gt_synthesises_from_spaceship(const CustomLTAndSpaceship&, const CustomLTAndSpaceship&));
123+
static_assert(__builtin_ge_synthesises_from_spaceship(const CustomLTAndSpaceship&, const CustomLTAndSpaceship&));
124+
125+
struct CustomLEAndSpaceship {
126+
int i;
127+
128+
friend auto operator<=>(CustomLEAndSpaceship lhs, CustomLEAndSpaceship rhs) {
129+
return rhs.i <=> lhs.i;
130+
}
131+
132+
friend auto operator<=(CustomLEAndSpaceship lhs, CustomLEAndSpaceship rhs) {
133+
return rhs.i < lhs.i;
134+
}
135+
};
136+
137+
static_assert(__builtin_lt_synthesises_from_spaceship(const CustomLEAndSpaceship&, const CustomLEAndSpaceship&));
138+
static_assert(!__builtin_le_synthesises_from_spaceship(const CustomLEAndSpaceship&, const CustomLEAndSpaceship&));
139+
static_assert(__builtin_gt_synthesises_from_spaceship(const CustomLEAndSpaceship&, const CustomLEAndSpaceship&));
140+
static_assert(__builtin_ge_synthesises_from_spaceship(const CustomLEAndSpaceship&, const CustomLEAndSpaceship&));
141+
142+
struct CustomGTAndSpaceship {
143+
int i;
144+
145+
friend auto operator<=>(CustomGTAndSpaceship lhs, CustomGTAndSpaceship rhs) {
146+
return rhs.i <=> lhs.i;
147+
}
148+
149+
friend auto operator>(CustomGTAndSpaceship lhs, CustomGTAndSpaceship rhs) {
150+
return rhs.i < lhs.i;
151+
}
152+
};
153+
154+
static_assert(__builtin_lt_synthesises_from_spaceship(const CustomGTAndSpaceship&, const CustomGTAndSpaceship&));
155+
static_assert(__builtin_le_synthesises_from_spaceship(const CustomGTAndSpaceship&, const CustomGTAndSpaceship&));
156+
static_assert(!__builtin_gt_synthesises_from_spaceship(const CustomGTAndSpaceship&, const CustomGTAndSpaceship&));
157+
static_assert(__builtin_ge_synthesises_from_spaceship(const CustomGTAndSpaceship&, const CustomGTAndSpaceship&));
158+
159+
struct CustomGEAndSpaceship {
160+
int i;
161+
162+
friend auto operator<=>(CustomGEAndSpaceship lhs, CustomGEAndSpaceship rhs) {
163+
return rhs.i <=> lhs.i;
164+
}
165+
166+
friend auto operator>=(CustomGEAndSpaceship lhs, CustomGEAndSpaceship rhs) {
167+
return rhs.i < lhs.i;
168+
}
169+
};
170+
171+
static_assert(__builtin_lt_synthesises_from_spaceship(const CustomGEAndSpaceship&, const CustomGEAndSpaceship&));
172+
static_assert(__builtin_le_synthesises_from_spaceship(const CustomGEAndSpaceship&, const CustomGEAndSpaceship&));
173+
static_assert(__builtin_gt_synthesises_from_spaceship(const CustomGEAndSpaceship&, const CustomGEAndSpaceship&));
174+
static_assert(!__builtin_ge_synthesises_from_spaceship(const CustomGEAndSpaceship&, const CustomGEAndSpaceship&));
175+
176+
struct DefaultedCmpAndSpaceship {
177+
int i;
178+
179+
friend auto operator<=>(DefaultedCmpAndSpaceship lhs, DefaultedCmpAndSpaceship rhs) {
180+
return rhs.i <=> lhs.i;
181+
}
182+
183+
friend bool operator<(DefaultedCmpAndSpaceship lhs, DefaultedCmpAndSpaceship rhs) = default;
184+
friend bool operator<=(DefaultedCmpAndSpaceship lhs, DefaultedCmpAndSpaceship rhs) = default;
185+
friend bool operator>(DefaultedCmpAndSpaceship lhs, DefaultedCmpAndSpaceship rhs) = default;
186+
friend bool operator>=(DefaultedCmpAndSpaceship lhs, DefaultedCmpAndSpaceship rhs) = default;
187+
};
188+
189+
// TODO: This should probably return true
190+
static_assert(!__builtin_lt_synthesises_from_spaceship(const DefaultedCmpAndSpaceship&, const DefaultedCmpAndSpaceship&));
191+
static_assert(!__builtin_le_synthesises_from_spaceship(const DefaultedCmpAndSpaceship&, const DefaultedCmpAndSpaceship&));
192+
static_assert(!__builtin_gt_synthesises_from_spaceship(const DefaultedCmpAndSpaceship&, const DefaultedCmpAndSpaceship&));
193+
static_assert(!__builtin_ge_synthesises_from_spaceship(const DefaultedCmpAndSpaceship&, const DefaultedCmpAndSpaceship&));
194+
195+
struct DifferentTypes {
196+
int i;
197+
198+
friend auto operator<=>(DifferentTypes lhs, int rhs) {
199+
return rhs <=> lhs.i;
200+
}
201+
};
202+
203+
static_assert(__builtin_lt_synthesises_from_spaceship(const DifferentTypes&, const int&));
204+
static_assert(__builtin_le_synthesises_from_spaceship(const DifferentTypes&, const int&));
205+
static_assert(__builtin_gt_synthesises_from_spaceship(const DifferentTypes&, const int&));
206+
static_assert(__builtin_ge_synthesises_from_spaceship(const DifferentTypes&, const int&));
207+
208+
// TODO: Should this return true? It's technically not synthesized from spaceship, but it behaves exactly as-if it was
209+
static_assert(!__builtin_lt_synthesises_from_spaceship(int, int));
210+
static_assert(!__builtin_le_synthesises_from_spaceship(int, int));
211+
static_assert(!__builtin_gt_synthesises_from_spaceship(int, int));
212+
static_assert(!__builtin_ge_synthesises_from_spaceship(int, int));

0 commit comments

Comments
 (0)