diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -1401,6 +1401,10 @@ * ``__array_extent(type, dim)`` (Embarcadero): The ``dim``'th array bound in the type ``type``, or ``0`` if ``dim >= __array_rank(type)``. +* ``__can_pass_in_regs`` (C++) + Returns whether a class can be passed in registers under the current + ABI. This type can only be applied to unqualified class types. + This is not a portable type trait. * ``__has_nothrow_assign`` (GNU, Microsoft, Embarcadero): Deprecated, use ``__is_nothrow_assignable`` instead. * ``__has_nothrow_move_assign`` (GNU, Microsoft): diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11794,4 +11794,8 @@ "change type of '%0' to '%select{std::span|std::array|std::span::iterator}1' to preserve bounds information">; def err_loongarch_builtin_requires_la32 : Error< "this builtin requires target: loongarch32">; + +def err_builtin_pass_in_regs_non_class : Error< + "argument %0 is not an unqualified class type">; + } // end of sema component. diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -523,6 +523,7 @@ TYPE_TRAIT_1(__is_nullptr, IsNullPointer, KEYCXX) TYPE_TRAIT_1(__is_scoped_enum, IsScopedEnum, KEYCXX) TYPE_TRAIT_1(__is_referenceable, IsReferenceable, KEYCXX) +TYPE_TRAIT_1(__can_pass_in_regs, CanPassInRegs, KEYCXX) TYPE_TRAIT_2(__reference_binds_to_temporary, ReferenceBindsToTemporary, KEYCXX) // Embarcadero Expression Traits diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -4885,6 +4885,7 @@ case UTT_IsLiteral: // By analogy, is_trivially_relocatable imposes the same constraints. case UTT_IsTriviallyRelocatable: + case UTT_CanPassInRegs: // Per the GCC type traits documentation, T shall be a complete type, cv void, // or an array of unknown bound. But GCC actually imposes the same constraints // as above. @@ -5373,6 +5374,11 @@ return T.isTriviallyRelocatableType(C); case UTT_IsReferenceable: return T.isReferenceable(); + case UTT_CanPassInRegs: + if (CXXRecordDecl *RD = T->getAsCXXRecordDecl(); RD && !T.hasQualifiers()) + return RD->canPassInRegisters(); + Self.Diag(KeyLoc, diag::err_builtin_pass_in_regs_non_class) << T; + return false; } } diff --git a/clang/test/SemaCXX/type-traits.cpp b/clang/test/SemaCXX/type-traits.cpp --- a/clang/test/SemaCXX/type-traits.cpp +++ b/clang/test/SemaCXX/type-traits.cpp @@ -3055,6 +3055,36 @@ } // namespace is_trivially_relocatable +namespace can_pass_in_regs { + +struct A { }; + +struct B { + ~B(); +}; + +struct C; // expected-note {{forward declaration}} + +union D { + int x; +}; + +static_assert(__can_pass_in_regs(A), ""); +static_assert(__can_pass_in_regs(A), ""); +static_assert(!__can_pass_in_regs(B), ""); +static_assert(__can_pass_in_regs(D), ""); + +void test_errors() { + (void)__can_pass_in_regs(const A); // expected-error {{not an unqualified class type}} + (void)__can_pass_in_regs(A&); // expected-error {{not an unqualified class type}} + (void)__can_pass_in_regs(A&&); // expected-error {{not an unqualified class type}} + (void)__can_pass_in_regs(const A&); // expected-error {{not an unqualified class type}} + (void)__can_pass_in_regs(C); // expected-error {{incomplete type}} + (void)__can_pass_in_regs(int); // expected-error {{not an unqualified class type}} + (void)__can_pass_in_regs(int&); // expected-error {{not an unqualified class type}} +} +} + struct S {}; template using remove_const_t = __remove_const(T);