diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index cbe59124d5b99..9767fde2c65a1 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -2049,6 +2049,9 @@ The following type trait primitives are supported by Clang. Those traits marked Returns true if a reference ``T`` can be copy-initialized from a temporary of type a non-cv-qualified ``U``. * ``__underlying_type`` (C++, GNU, Microsoft) +* ``__builtin_lt_synthesises_from_spaceship``, ``__builtin_gt_synthesises_from_spaceship``, + ``__builtin_le_synthesises_from_spaceship``, ``__builtin_ge_synthesises_from_spaceship`` (Clang): + These builtins can be used to determine whether the corresponding operator is synthesised from a spaceship operator. In addition, the following expression traits are supported: diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 046e6a51a1ebd..39fd1e396cdb8 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -109,6 +109,11 @@ What's New in Clang |release|? C++ Language Changes -------------------- +- A new family of builtins ``__builtin_*_synthesises_from_spaceship`` has been added. These can be queried to know + whether the ``<`` (``lt``), ``>`` (``gt``), ``<=`` (``le``), or ``>=`` (``ge``) operators are synthesised from a + ``<=>``. This makes it possible to optimize certain facilities by using the ``<=>`` operation directly instead of + doing multiple comparisons. + C++2c Feature Support ^^^^^^^^^^^^^^^^^^^^^ @@ -241,7 +246,7 @@ Improvements to Clang's diagnostics "format specifies type 'unsigned int' but the argument has type 'int', which differs in signedness [-Wformat-signedness]" "signedness of format specifier 'u' is incompatible with 'c' [-Wformat-signedness]" and the API-visible diagnostic id will be appropriate. - + - Fixed false positives in ``-Waddress-of-packed-member`` diagnostics when potential misaligned members get processed before they can get discarded. (#GH144729) @@ -275,7 +280,7 @@ Bug Fixes in This Version ------------------------- - Fix a crash when marco name is empty in ``#pragma push_macro("")`` or ``#pragma pop_macro("")``. (#GH149762). -- Fix a crash in variable length array (e.g. ``int a[*]``) function parameter type +- Fix a crash in variable length array (e.g. ``int a[*]``) function parameter type being used in ``_Countof`` expression. (#GH152826). - ``-Wunreachable-code`` now diagnoses tautological or contradictory comparisons such as ``x != 0 || x != 1.0`` and ``x == 0 && x == 1.0`` on @@ -360,7 +365,7 @@ X86 Support arithmetic can now be used in C++ constant expressions. - Some SSE, AVX and AVX512 intrinsics have been converted to wrap generic __builtin intrinsics. -- NOTE: Please avoid use of the __builtin_ia32_* intrinsics - these are not +- NOTE: Please avoid use of the __builtin_ia32_* intrinsics - these are not guaranteed to exist in future releases, or match behaviour with previous releases of clang or other compilers. diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index 94e72fea56a68..9d1a23d1af218 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -552,6 +552,10 @@ TYPE_TRAIT_1(__can_pass_in_regs, CanPassInRegs, KEYCXX) TYPE_TRAIT_2(__reference_binds_to_temporary, ReferenceBindsToTemporary, KEYCXX) TYPE_TRAIT_2(__reference_constructs_from_temporary, ReferenceConstructsFromTemporary, KEYCXX) TYPE_TRAIT_2(__reference_converts_from_temporary, ReferenceConvertsFromTemporary, KEYCXX) +TYPE_TRAIT_2(__builtin_lt_synthesises_from_spaceship, LtSynthesisesFromSpaceship, KEYCXX) +TYPE_TRAIT_2(__builtin_le_synthesises_from_spaceship, LeSynthesisesFromSpaceship, KEYCXX) +TYPE_TRAIT_2(__builtin_gt_synthesises_from_spaceship, GtSynthesisesFromSpaceship, KEYCXX) +TYPE_TRAIT_2(__builtin_ge_synthesises_from_spaceship, GeSynthesisesFromSpaceship, KEYCXX) // IsDeducible is only used internally by clang for CTAD implementation and // is not exposed to users. TYPE_TRAIT_2(/*EmptySpellingName*/, IsDeducible, KEYCXX) diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp index 37552331478f1..b77975945c18c 100644 --- a/clang/lib/Sema/SemaTypeTraits.cpp +++ b/clang/lib/Sema/SemaTypeTraits.cpp @@ -1825,6 +1825,51 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, return Self.HLSL().IsScalarizedLayoutCompatible(LhsT, RhsT); } + case BTT_LtSynthesisesFromSpaceship: + case BTT_LeSynthesisesFromSpaceship: + case BTT_GtSynthesisesFromSpaceship: + case BTT_GeSynthesisesFromSpaceship: { + EnterExpressionEvaluationContext UnevaluatedContext( + Self, Sema::ExpressionEvaluationContext::Unevaluated); + Sema::SFINAETrap SFINAE(Self, /*ForValidityCheck=*/true); + Sema::ContextRAII TUContext(Self, Self.Context.getTranslationUnitDecl()); + + OpaqueValueExpr LHS(KeyLoc, LhsT.getNonReferenceType(), + LhsT->isLValueReferenceType() ? ExprValueKind::VK_LValue + : LhsT->isRValueReferenceType() + ? ExprValueKind::VK_XValue + : ExprValueKind::VK_PRValue); + OpaqueValueExpr RHS(KeyLoc, RhsT.getNonReferenceType(), + RhsT->isLValueReferenceType() ? ExprValueKind::VK_LValue + : RhsT->isRValueReferenceType() + ? ExprValueKind::VK_XValue + : ExprValueKind::VK_PRValue); + + auto OpKind = [&] { + switch (BTT) { + case BTT_LtSynthesisesFromSpaceship: + return BinaryOperatorKind::BO_LT; + case BTT_LeSynthesisesFromSpaceship: + return BinaryOperatorKind::BO_LE; + case BTT_GtSynthesisesFromSpaceship: + return BinaryOperatorKind::BO_GT; + case BTT_GeSynthesisesFromSpaceship: + return BinaryOperatorKind::BO_GE; + default: + llvm_unreachable("Trying to Synthesize non-comparison operator?"); + } + }(); + + UnresolvedSet<16> Functions; + Self.LookupBinOp(Self.TUScope, KeyLoc, OpKind, Functions); + + ExprResult Result = + Self.CreateOverloadedBinOp(KeyLoc, OpKind, Functions, &LHS, &RHS); + if (Result.isInvalid() || SFINAE.hasErrorOccurred()) + return false; + + return isa(Result.get()); + } default: llvm_unreachable("not a BTT"); } diff --git a/clang/test/SemaCXX/type-trait-synthesises-from-spaceship.cpp b/clang/test/SemaCXX/type-trait-synthesises-from-spaceship.cpp new file mode 100644 index 0000000000000..ba581475bb4c7 --- /dev/null +++ b/clang/test/SemaCXX/type-trait-synthesises-from-spaceship.cpp @@ -0,0 +1,212 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s + +static_assert(!__builtin_lt_synthesises_from_spaceship()); // expected-error {{expected a type}} +static_assert(!__builtin_lt_synthesises_from_spaceship(int)); // expected-error {{type trait requires 2 arguments; have 1 argument}} +static_assert(!__builtin_lt_synthesises_from_spaceship(int, int, int)); // expected-error {{type trait requires 2 arguments; have 3 argument}} +static_assert(!__builtin_lt_synthesises_from_spaceship(int, 0)); // expected-error {{expected a type}} + +static_assert(!__builtin_le_synthesises_from_spaceship()); // expected-error {{expected a type}} +static_assert(!__builtin_le_synthesises_from_spaceship(int)); // expected-error {{type trait requires 2 arguments; have 1 argument}} +static_assert(!__builtin_le_synthesises_from_spaceship(int, int, int)); // expected-error {{type trait requires 2 arguments; have 3 argument}} +static_assert(!__builtin_le_synthesises_from_spaceship(int, 0)); // expected-error {{expected a type}} + +static_assert(!__builtin_gt_synthesises_from_spaceship()); // expected-error {{expected a type}} +static_assert(!__builtin_gt_synthesises_from_spaceship(int)); // expected-error {{type trait requires 2 arguments; have 1 argument}} +static_assert(!__builtin_gt_synthesises_from_spaceship(int, int, int)); // expected-error {{type trait requires 2 arguments; have 3 argument}} +static_assert(!__builtin_gt_synthesises_from_spaceship(int, 0)); // expected-error {{expected a type}} + +static_assert(!__builtin_ge_synthesises_from_spaceship()); // expected-error {{expected a type}} +static_assert(!__builtin_ge_synthesises_from_spaceship(int)); // expected-error {{type trait requires 2 arguments; have 1 argument}} +static_assert(!__builtin_ge_synthesises_from_spaceship(int, int, int)); // expected-error {{type trait requires 2 arguments; have 3 argument}} +static_assert(!__builtin_ge_synthesises_from_spaceship(int, 0)); // expected-error {{expected a type}} + +namespace std { + struct strong_ordering { + int n; + constexpr operator int() const { return n; } + static const strong_ordering less, equal, greater; + }; + constexpr strong_ordering strong_ordering::less = {-1}; + constexpr strong_ordering strong_ordering::equal = {0}; + constexpr strong_ordering strong_ordering::greater = {1}; +} + +struct DefaultSpaceship { + friend auto operator<=>(DefaultSpaceship, DefaultSpaceship) = default; +}; + +static_assert(__builtin_lt_synthesises_from_spaceship(const DefaultSpaceship&, const DefaultSpaceship&)); +static_assert(__builtin_le_synthesises_from_spaceship(const DefaultSpaceship&, const DefaultSpaceship&)); +static_assert(__builtin_gt_synthesises_from_spaceship(const DefaultSpaceship&, const DefaultSpaceship&)); +static_assert(__builtin_ge_synthesises_from_spaceship(const DefaultSpaceship&, const DefaultSpaceship&)); + +struct CustomSpaceship { + int i; + + friend auto operator<=>(CustomSpaceship lhs, CustomSpaceship rhs) { + return rhs.i <=> lhs.i; + } +}; + +static_assert(__builtin_lt_synthesises_from_spaceship(const CustomSpaceship&, const CustomSpaceship&)); +static_assert(__builtin_le_synthesises_from_spaceship(const CustomSpaceship&, const CustomSpaceship&)); +static_assert(__builtin_gt_synthesises_from_spaceship(const CustomSpaceship&, const CustomSpaceship&)); +static_assert(__builtin_ge_synthesises_from_spaceship(const CustomSpaceship&, const CustomSpaceship&)); + +struct CustomLT { + int i; + + friend auto operator<(CustomLT lhs, CustomLT rhs) { + return rhs.i < lhs.i; + } +}; + +static_assert(!__builtin_lt_synthesises_from_spaceship(const CustomLT&, const CustomLT&)); +static_assert(!__builtin_le_synthesises_from_spaceship(const CustomLT&, const CustomLT&)); +static_assert(!__builtin_gt_synthesises_from_spaceship(const CustomLT&, const CustomLT&)); +static_assert(!__builtin_ge_synthesises_from_spaceship(const CustomLT&, const CustomLT&)); + +struct CustomLE { + int i; + + friend auto operator<=(CustomLE lhs, CustomLE rhs) { + return rhs.i < lhs.i; + } +}; + +static_assert(!__builtin_lt_synthesises_from_spaceship(const CustomLE&, const CustomLE&)); +static_assert(!__builtin_le_synthesises_from_spaceship(const CustomLE&, const CustomLE&)); +static_assert(!__builtin_gt_synthesises_from_spaceship(const CustomLE&, const CustomLE&)); +static_assert(!__builtin_ge_synthesises_from_spaceship(const CustomLE&, const CustomLE&)); + +struct CustomGT { + int i; + + friend auto operator>(CustomGT lhs, CustomGT rhs) { + return rhs.i < lhs.i; + } +}; + +static_assert(!__builtin_lt_synthesises_from_spaceship(const CustomGT&, const CustomGT&)); +static_assert(!__builtin_le_synthesises_from_spaceship(const CustomGT&, const CustomGT&)); +static_assert(!__builtin_gt_synthesises_from_spaceship(const CustomGT&, const CustomGT&)); +static_assert(!__builtin_ge_synthesises_from_spaceship(const CustomGT&, const CustomGT&)); + +struct CustomGE { + int i; + + friend auto operator>=(CustomGE lhs, CustomGE rhs) { + return rhs.i < lhs.i; + } +}; + +static_assert(!__builtin_lt_synthesises_from_spaceship(const CustomGE&, const CustomGE&)); +static_assert(!__builtin_le_synthesises_from_spaceship(const CustomGE&, const CustomGE&)); +static_assert(!__builtin_gt_synthesises_from_spaceship(const CustomGE&, const CustomGE&)); +static_assert(!__builtin_ge_synthesises_from_spaceship(const CustomGE&, const CustomGE&)); + +struct CustomLTAndSpaceship { + int i; + + friend auto operator<=>(CustomLTAndSpaceship lhs, CustomLTAndSpaceship rhs) { + return rhs.i <=> lhs.i; + } + + friend auto operator<(CustomLTAndSpaceship lhs, CustomLTAndSpaceship rhs) { + return rhs.i < lhs.i; + } +}; + +static_assert(!__builtin_lt_synthesises_from_spaceship(const CustomLTAndSpaceship&, const CustomLTAndSpaceship&)); +static_assert(__builtin_le_synthesises_from_spaceship(const CustomLTAndSpaceship&, const CustomLTAndSpaceship&)); +static_assert(__builtin_gt_synthesises_from_spaceship(const CustomLTAndSpaceship&, const CustomLTAndSpaceship&)); +static_assert(__builtin_ge_synthesises_from_spaceship(const CustomLTAndSpaceship&, const CustomLTAndSpaceship&)); + +struct CustomLEAndSpaceship { + int i; + + friend auto operator<=>(CustomLEAndSpaceship lhs, CustomLEAndSpaceship rhs) { + return rhs.i <=> lhs.i; + } + + friend auto operator<=(CustomLEAndSpaceship lhs, CustomLEAndSpaceship rhs) { + return rhs.i < lhs.i; + } +}; + +static_assert(__builtin_lt_synthesises_from_spaceship(const CustomLEAndSpaceship&, const CustomLEAndSpaceship&)); +static_assert(!__builtin_le_synthesises_from_spaceship(const CustomLEAndSpaceship&, const CustomLEAndSpaceship&)); +static_assert(__builtin_gt_synthesises_from_spaceship(const CustomLEAndSpaceship&, const CustomLEAndSpaceship&)); +static_assert(__builtin_ge_synthesises_from_spaceship(const CustomLEAndSpaceship&, const CustomLEAndSpaceship&)); + +struct CustomGTAndSpaceship { + int i; + + friend auto operator<=>(CustomGTAndSpaceship lhs, CustomGTAndSpaceship rhs) { + return rhs.i <=> lhs.i; + } + + friend auto operator>(CustomGTAndSpaceship lhs, CustomGTAndSpaceship rhs) { + return rhs.i < lhs.i; + } +}; + +static_assert(__builtin_lt_synthesises_from_spaceship(const CustomGTAndSpaceship&, const CustomGTAndSpaceship&)); +static_assert(__builtin_le_synthesises_from_spaceship(const CustomGTAndSpaceship&, const CustomGTAndSpaceship&)); +static_assert(!__builtin_gt_synthesises_from_spaceship(const CustomGTAndSpaceship&, const CustomGTAndSpaceship&)); +static_assert(__builtin_ge_synthesises_from_spaceship(const CustomGTAndSpaceship&, const CustomGTAndSpaceship&)); + +struct CustomGEAndSpaceship { + int i; + + friend auto operator<=>(CustomGEAndSpaceship lhs, CustomGEAndSpaceship rhs) { + return rhs.i <=> lhs.i; + } + + friend auto operator>=(CustomGEAndSpaceship lhs, CustomGEAndSpaceship rhs) { + return rhs.i < lhs.i; + } +}; + +static_assert(__builtin_lt_synthesises_from_spaceship(const CustomGEAndSpaceship&, const CustomGEAndSpaceship&)); +static_assert(__builtin_le_synthesises_from_spaceship(const CustomGEAndSpaceship&, const CustomGEAndSpaceship&)); +static_assert(__builtin_gt_synthesises_from_spaceship(const CustomGEAndSpaceship&, const CustomGEAndSpaceship&)); +static_assert(!__builtin_ge_synthesises_from_spaceship(const CustomGEAndSpaceship&, const CustomGEAndSpaceship&)); + +struct DefaultedCmpAndSpaceship { + int i; + + friend auto operator<=>(DefaultedCmpAndSpaceship lhs, DefaultedCmpAndSpaceship rhs) { + return rhs.i <=> lhs.i; + } + + friend bool operator<(DefaultedCmpAndSpaceship lhs, DefaultedCmpAndSpaceship rhs) = default; + friend bool operator<=(DefaultedCmpAndSpaceship lhs, DefaultedCmpAndSpaceship rhs) = default; + friend bool operator>(DefaultedCmpAndSpaceship lhs, DefaultedCmpAndSpaceship rhs) = default; + friend bool operator>=(DefaultedCmpAndSpaceship lhs, DefaultedCmpAndSpaceship rhs) = default; +}; + +// TODO: This should probably return true +static_assert(!__builtin_lt_synthesises_from_spaceship(const DefaultedCmpAndSpaceship&, const DefaultedCmpAndSpaceship&)); +static_assert(!__builtin_le_synthesises_from_spaceship(const DefaultedCmpAndSpaceship&, const DefaultedCmpAndSpaceship&)); +static_assert(!__builtin_gt_synthesises_from_spaceship(const DefaultedCmpAndSpaceship&, const DefaultedCmpAndSpaceship&)); +static_assert(!__builtin_ge_synthesises_from_spaceship(const DefaultedCmpAndSpaceship&, const DefaultedCmpAndSpaceship&)); + +struct DifferentTypes { + int i; + + friend auto operator<=>(DifferentTypes lhs, int rhs) { + return rhs <=> lhs.i; + } +}; + +static_assert(__builtin_lt_synthesises_from_spaceship(const DifferentTypes&, const int&)); +static_assert(__builtin_le_synthesises_from_spaceship(const DifferentTypes&, const int&)); +static_assert(__builtin_gt_synthesises_from_spaceship(const DifferentTypes&, const int&)); +static_assert(__builtin_ge_synthesises_from_spaceship(const DifferentTypes&, const int&)); + +// TODO: Should this return true? It's technically not synthesized from spaceship, but it behaves exactly as-if it was +static_assert(!__builtin_lt_synthesises_from_spaceship(int, int)); +static_assert(!__builtin_le_synthesises_from_spaceship(int, int)); +static_assert(!__builtin_gt_synthesises_from_spaceship(int, int)); +static_assert(!__builtin_ge_synthesises_from_spaceship(int, int));