rsmith created this revision. rsmith added a reviewer: aaron.ballman. Herald added subscribers: cfe-commits, kristina, jfb. Herald added a project: clang.
Previously __has_builtin(__builtin_*) would return false for __builtin_*s that we modeled as keywords rather than as functions (because they take type arguments). With this patch, all builtins that are called with function-call-like syntax return true from __has_builtin (covering __builtin_* and also the __is_* and __has_* type traits and the handful of similar builtins without such a prefix). Update the documentation on __has_builtin and on type traits to match. While doing this I noticed the type trait documentation was out of date and incomplete; that's fixed here too. Repository: rC Clang https://reviews.llvm.org/D66100 Files: docs/LanguageExtensions.rst include/clang/Basic/Features.def include/clang/Basic/TokenKinds.def lib/Lex/PPMacroExpansion.cpp test/Preprocessor/feature_tests.c test/Preprocessor/feature_tests.cpp
Index: test/Preprocessor/feature_tests.cpp =================================================================== --- /dev/null +++ test/Preprocessor/feature_tests.cpp @@ -0,0 +1,43 @@ +// RUN: %clang_cc1 %s -triple=i686-apple-darwin9 -verify -DVERIFY +// expected-no-diagnostics + +#ifndef __has_feature +#error Should have __has_feature +#endif + +#if __has_feature(something_we_dont_have) +#error Bad +#endif + +#if !__has_builtin(__builtin_huge_val) || \ + !__has_builtin(__builtin_shufflevector) || \ + !__has_builtin(__builtin_convertvector) || \ + !__has_builtin(__builtin_trap) || \ + !__has_builtin(__c11_atomic_init) || \ + !__has_builtin(__builtin_launder) || \ + !__has_feature(attribute_analyzer_noreturn) || \ + !__has_feature(attribute_overloadable) +#error Clang should have these +#endif + +// These are technically implemented as keywords, but __has_builtin should +// still return true. +#if !__has_builtin(__builtin_LINE) || \ + !__has_builtin(__builtin_FILE) || \ + !__has_builtin(__builtin_FUNCTION) || \ + !__has_builtin(__builtin_COLUMN) || \ + !__has_builtin(__array_rank) || \ + !__has_builtin(__underlying_type) || \ + !__has_builtin(__is_trivial) || \ + !__has_builtin(__has_unique_object_representations) +#error Clang should have these +#endif + +// This is a C-only builtin. +#if __has_builtin(__builtin_types_compatible_p) +#error Clang should not have this in C++ mode +#endif + +#if __has_builtin(__builtin_insanity) +#error Clang should not have this +#endif Index: test/Preprocessor/feature_tests.c =================================================================== --- test/Preprocessor/feature_tests.c +++ test/Preprocessor/feature_tests.c @@ -25,10 +25,18 @@ #if !__has_builtin(__builtin_LINE) || \ !__has_builtin(__builtin_FILE) || \ !__has_builtin(__builtin_FUNCTION) || \ - !__has_builtin(__builtin_COLUMN) + !__has_builtin(__builtin_COLUMN) || \ + !__has_builtin(__builtin_types_compatible_p) #error Clang should have these #endif +// These are C++-only builtins. +#if __has_builtin(__array_rank) || \ + __has_builtin(__underlying_type) || \ + __has_builtin(__is_trivial) +#error Clang should not have these in C mode +#endif + #if __has_builtin(__builtin_insanity) #error Clang should not have this #endif Index: lib/Lex/PPMacroExpansion.cpp =================================================================== --- lib/Lex/PPMacroExpansion.cpp +++ lib/Lex/PPMacroExpansion.cpp @@ -1617,21 +1617,38 @@ return true; } return true; + } else if (II->getTokenID() != tok::identifier || + II->hasRevertedTokenIDToIdentifier()) { + // Treat all keywords that introduce a custom syntax of the form + // + // '__some_keyword' '(' [...] ')' + // + // as being "builtin functions", even if the syntax isn't a valid + // function call (for example, because the builtin takes a type + // argument). + if (II->getName().startswith("__builtin_") || + II->getName().startswith("__is_") || + II->getName().startswith("__has_")) + return true; + return llvm::StringSwitch<bool>(II->getName()) + .Case("__array_rank", true) + .Case("__array_extent", true) + .Case("__reference_binds_to_temporary", true) + .Case("__underlying_type", true) + .Default(false); } else { return llvm::StringSwitch<bool>(II->getName()) - .Case("__make_integer_seq", LangOpts.CPlusPlus) - .Case("__type_pack_element", LangOpts.CPlusPlus) - .Case("__builtin_available", true) - .Case("__is_target_arch", true) - .Case("__is_target_vendor", true) - .Case("__is_target_os", true) - .Case("__is_target_environment", true) - .Case("__builtin_LINE", true) - .Case("__builtin_FILE", true) - .Case("__builtin_FUNCTION", true) - .Case("__builtin_COLUMN", true) - .Case("__builtin_bit_cast", true) - .Default(false); + // Report builtin templates as being builtins. + .Case("__make_integer_seq", LangOpts.CPlusPlus) + .Case("__type_pack_element", LangOpts.CPlusPlus) + // Likewise for some builtin preprocessor macros. + // FIXME: This is inconsistent; we usually suggest detecting + // builtin macros via #ifdef. Don't add more cases here. + .Case("__is_target_arch", true) + .Case("__is_target_vendor", true) + .Case("__is_target_os", true) + .Case("__is_target_environment", true) + .Default(false); } }); } else if (II == Ident__is_identifier) { Index: include/clang/Basic/TokenKinds.def =================================================================== --- include/clang/Basic/TokenKinds.def +++ include/clang/Basic/TokenKinds.def @@ -449,16 +449,18 @@ // MSVC14.0 / VS2015 Type Traits TYPE_TRAIT_2(__is_assignable, IsAssignable, KEYCXX) +// MSVC Type Traits of unknown vintage +TYPE_TRAIT_1(__has_nothrow_move_assign, HasNothrowMoveAssign, KEYCXX) +TYPE_TRAIT_1(__has_trivial_move_assign, HasTrivialMoveAssign, KEYCXX) +TYPE_TRAIT_1(__has_trivial_move_constructor, HasTrivialMoveConstructor, KEYCXX) + // GNU and MS Type Traits TYPE_TRAIT_1(__has_nothrow_assign, HasNothrowAssign, KEYCXX) -TYPE_TRAIT_1(__has_nothrow_move_assign, HasNothrowMoveAssign, KEYCXX) TYPE_TRAIT_1(__has_nothrow_copy, HasNothrowCopy, KEYCXX) TYPE_TRAIT_1(__has_nothrow_constructor, HasNothrowConstructor, KEYCXX) TYPE_TRAIT_1(__has_trivial_assign, HasTrivialAssign, KEYCXX) -TYPE_TRAIT_1(__has_trivial_move_assign, HasTrivialMoveAssign, KEYCXX) TYPE_TRAIT_1(__has_trivial_copy, HasTrivialCopy, KEYCXX) TYPE_TRAIT_1(__has_trivial_constructor, HasTrivialDefaultConstructor, KEYCXX) -TYPE_TRAIT_1(__has_trivial_move_constructor, HasTrivialMoveConstructor, KEYCXX) TYPE_TRAIT_1(__has_trivial_destructor, HasTrivialDestructor, KEYCXX) TYPE_TRAIT_1(__has_virtual_destructor, HasVirtualDestructor, KEYCXX) TYPE_TRAIT_1(__is_abstract, IsAbstract, KEYCXX) @@ -475,17 +477,18 @@ ALIAS("__is_literal_type", __is_literal, KEYCXX) TYPE_TRAIT_1(__is_pod, IsPOD, KEYCXX) TYPE_TRAIT_1(__is_polymorphic, IsPolymorphic, KEYCXX) +TYPE_TRAIT_1(__is_standard_layout, IsStandardLayout, KEYCXX) TYPE_TRAIT_1(__is_trivial, IsTrivial, KEYCXX) +TYPE_TRAIT_2(__is_trivially_assignable, IsTriviallyAssignable, KEYCXX) +TYPE_TRAIT_N(__is_trivially_constructible, IsTriviallyConstructible, KEYCXX) +TYPE_TRAIT_1(__is_trivially_copyable, IsTriviallyCopyable, KEYCXX) TYPE_TRAIT_1(__is_union, IsUnion, KEYCXX) TYPE_TRAIT_1(__has_unique_object_representations, HasUniqueObjectRepresentations, KEYCXX) +KEYWORD(__underlying_type , KEYCXX) // Clang-only C++ Type Traits -TYPE_TRAIT_N(__is_trivially_constructible, IsTriviallyConstructible, KEYCXX) -TYPE_TRAIT_1(__is_trivially_copyable, IsTriviallyCopyable, KEYCXX) -TYPE_TRAIT_2(__is_trivially_assignable, IsTriviallyAssignable, KEYCXX) TYPE_TRAIT_2(__reference_binds_to_temporary, ReferenceBindsToTemporary, KEYCXX) -KEYWORD(__underlying_type , KEYCXX) // Embarcadero Expression Traits KEYWORD(__is_lvalue_expr , KEYCXX) @@ -512,7 +515,6 @@ TYPE_TRAIT_1(__is_member_pointer, IsMemberPointer, KEYCXX) TYPE_TRAIT_1(__is_const, IsConst, KEYCXX) TYPE_TRAIT_1(__is_volatile, IsVolatile, KEYCXX) -TYPE_TRAIT_1(__is_standard_layout, IsStandardLayout, KEYCXX) TYPE_TRAIT_1(__is_signed, IsSigned, KEYCXX) TYPE_TRAIT_1(__is_unsigned, IsUnsigned, KEYCXX) Index: include/clang/Basic/Features.def =================================================================== --- include/clang/Basic/Features.def +++ include/clang/Basic/Features.def @@ -187,7 +187,7 @@ // FEATURE(raw_invocation_type, LangOpts.CPlusPlus) // Type traits // N.B. Additional type traits should not be added to the following list. -// Instead, they should be detected by has_extension. +// Instead, they should be detected by has_builtin. FEATURE(has_nothrow_assign, LangOpts.CPlusPlus) FEATURE(has_nothrow_copy, LangOpts.CPlusPlus) FEATURE(has_nothrow_constructor, LangOpts.CPlusPlus) Index: docs/LanguageExtensions.rst =================================================================== --- docs/LanguageExtensions.rst +++ docs/LanguageExtensions.rst @@ -38,7 +38,9 @@ ----------------- This function-like macro takes a single identifier argument that is the name of -a builtin function. It evaluates to 1 if the builtin is supported or 0 if not. +a builtin function, a builtin pseudo-function (taking one or more type +arguments), or a builtin template. +It evaluates to 1 if the builtin is supported or 0 if not. It can be used like this: .. code-block:: c++ @@ -55,6 +57,14 @@ #endif ... +.. note:: + + Prior to Clang 10, ``__has_builtin`` could not be used to detect most builtin + pseudo-functions. + + ``__has_builtin`` should not be used to detect support for a built-in macro; + use ``#ifdef`` instead. + .. _langext-__has_feature-__has_extension: ``__has_feature`` and ``__has_extension`` @@ -1041,8 +1051,8 @@ More information could be found `here <https://clang.llvm.org/docs/Modules.html>`_. -Checks for Type Trait Primitives -================================ +Type Trait Primitives +===================== Type trait primitives are special builtin constant expressions that can be used by the standard C++ library to facilitate or simplify the implementation of @@ -1058,75 +1068,180 @@ Clang supports the `GNU C++ type traits <https://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html>`_ and a subset of the -`Microsoft Visual C++ Type traits -<https://msdn.microsoft.com/en-us/library/ms177194(v=VS.100).aspx>`_. +`Microsoft Visual C++ type traits +<https://msdn.microsoft.com/en-us/library/ms177194(v=VS.100).aspx>`_, +as well as nearly all of the +`Embarcadero C++ type traits +<http://docwiki.embarcadero.com/RADStudio/Rio/en/Type_Trait_Functions_(C%2B%2B11)_Index>`_. -Feature detection is supported only for some of the primitives at present. User -code should not use these checks because they bear no direct relation to the -actual set of type traits supported by the C++ standard library. +The following type trait primitives are supported by Clang. Those traits marked +(C++) provide implementations for type traits specified by the C++ standard; +``__X(...)`` has the same semantics and constraints as the corresponding +``std::X_t<...>`` or ``std::X_v<...>`` type trait. -For type trait ``__X``, ``__has_extension(X)`` indicates the presence of the -type trait primitive in the compiler. A simplistic usage example as might be -seen in standard C++ headers follows: - -.. code-block:: c++ - - #if __has_extension(is_convertible_to) - template<typename From, typename To> - struct is_convertible_to { - static const bool value = __is_convertible_to(From, To); - }; - #else - // Emulate type trait for compatibility with other compilers. - #endif - -The following type trait primitives are supported by Clang: - -* ``__has_nothrow_assign`` (GNU, Microsoft) -* ``__has_nothrow_copy`` (GNU, Microsoft) -* ``__has_nothrow_constructor`` (GNU, Microsoft) -* ``__has_trivial_assign`` (GNU, Microsoft) -* ``__has_trivial_copy`` (GNU, Microsoft) -* ``__has_trivial_constructor`` (GNU, Microsoft) -* ``__has_trivial_destructor`` (GNU, Microsoft) -* ``__has_virtual_destructor`` (GNU, Microsoft) -* ``__is_abstract`` (GNU, Microsoft) -* ``__is_aggregate`` (GNU, Microsoft) -* ``__is_base_of`` (GNU, Microsoft) -* ``__is_class`` (GNU, Microsoft) -* ``__is_convertible_to`` (Microsoft) -* ``__is_empty`` (GNU, Microsoft) -* ``__is_enum`` (GNU, Microsoft) -* ``__is_interface_class`` (Microsoft) -* ``__is_pod`` (GNU, Microsoft) -* ``__is_polymorphic`` (GNU, Microsoft) -* ``__is_union`` (GNU, Microsoft) -* ``__is_literal(type)``: Determines whether the given type is a literal type -* ``__is_final``: Determines whether the given type is declared with a - ``final`` class-virt-specifier. -* ``__underlying_type(type)``: Retrieves the underlying type for a given - ``enum`` type. This trait is required to implement the C++11 standard - library. -* ``__is_trivially_assignable(totype, fromtype)``: Determines whether a value - of type ``totype`` can be assigned to from a value of type ``fromtype`` such - that no non-trivial functions are called as part of that assignment. This - trait is required to implement the C++11 standard library. -* ``__is_trivially_constructible(type, argtypes...)``: Determines whether a - value of type ``type`` can be direct-initialized with arguments of types - ``argtypes...`` such that no non-trivial functions are called as part of - that initialization. This trait is required to implement the C++11 standard - library. -* ``__is_destructible`` (MSVC 2013) -* ``__is_nothrow_destructible`` (MSVC 2013) -* ``__is_nothrow_assignable`` (MSVC 2013, clang) -* ``__is_constructible`` (MSVC 2013, clang) -* ``__is_nothrow_constructible`` (MSVC 2013, clang) -* ``__is_assignable`` (MSVC 2015, clang) +* ``__array_rank(type)`` (Embarcadero): + Returns the number of levels of array in the type ``type``: + ``0`` if ``type`` is not an array type, and + ``__array_rank(element) + 1`` if ``type`` is an array of ``element``. +* ``__array_extent(type, dim)`` (Embarcadero): + The ``dim``'th array bound in the type ``type``, or ``0`` if + ``dim >= __array_rank(type)``. +* ``__has_nothrow_assign`` (GNU, Microsoft, Embarcadero): + Deprecated, use ``__is_nothrow_assignable`` instead. +* ``__has_nothrow_move_assign`` (GNU, Microsoft): + Deprecated, use ``__is_nothrow_assignable`` instead. +* ``__has_nothrow_copy`` (GNU, Microsoft): + Deprecated, use ``__is_nothrow_constructible`` instead. +* ``__has_nothrow_constructor`` (GNU, Microsoft): + Deprecated, use ``__is_nothrow_constructible`` instead. +* ``__has_trivial_assign`` (GNU, Microsoft, Embarcadero): + Deprecated, use ``__is_trivially_assignable`` instead. +* ``__has_trivial_move_assign`` (GNU, Microsoft): + Deprecated, use ``__is_trivially_assignable`` instead. +* ``__has_trivial_copy`` (GNU, Microsoft): + Deprecated, use ``__is_trivially_constructible`` instead. +* ``__has_trivial_constructor`` (GNU, Microsoft): + Deprecated, use ``__is_trivially_constructible`` instead. +* ``__has_trivial_move_constructor`` (GNU, Microsoft): + Deprecated, use ``__is_trivially_constructible`` instead. +* ``__has_trivial_destructor`` (GNU, Microsoft, Embarcadero): + Deprecated, use ``__is_trivially_destructible`` instead. +* ``__has_unique_object_representations`` (C++, GNU) +* ``__has_virtual_destructor`` (C++, GNU, Microsoft, Embarcadero) +* ``__is_abstract`` (C++, GNU, Microsoft, Embarcadero) +* ``__is_aggregate`` (C++, GNU, Microsoft) +* ``__is_arithmetic`` (C++, Embarcadero) +* ``__is_array`` (C++, Embarcadero) +* ``__is_assignable`` (C++, MSVC 2015) +* ``__is_base_of`` (C++, GNU, Microsoft, Embarcadero) +* ``__is_class`` (C++, GNU, Microsoft, Embarcadero) +* ``__is_complete_type(type)`` (Embarcadero): + Return ``true`` if ``type`` is a complete type. + Warning: this trait is dangerous, because it can return different values at + different points in the same program. +* ``__is_compound`` (C++, Embarcadero) +* ``__is_const`` (C++, Embarcadero) +* ``__is_constructible`` (C++, MSVC 2013) +* ``__is_convertible`` (C++, Embarcadero) +* ``__is_convertible_to`` (Microsoft): + Synonym for ``__is_convertible``. +* ``__is_destructible`` (C++, MSVC 2013): + Only available in ``-fms-extensions`` mode. +* ``__is_empty`` (C++, GNU, Microsoft, Embarcadero) +* ``__is_enum`` (C++, GNU, Microsoft, Embarcadero) +* ``__is_final`` (C++, GNU, Microsoft) +* ``__is_floating_point`` (C++, Embarcadero) +* ``__is_function`` (C++, Embarcadero) +* ``__is_fundamental`` (C++, Embarcadero) +* ``__is_integral`` (C++, Embarcadero) +* ``__is_interface_class`` (Microsoft): + Returns ``false``, even for types defined with ``__interface``. +* ``__is_literal`` (Clang): + Synonym for ``__is_literal_type``. +* ``__is_literal_type`` (C++, GNU, Microsoft): + Note, the corresponding standard trait was deprecated in C++17 + and removed in C++20. +* ``__is_lvalue_reference`` (C++, Embarcadero) +* ``__is_member_object_pointer`` (C++, Embarcadero) +* ``__is_member_function_pointer`` (C++, Embarcadero) +* ``__is_member_pointer`` (C++, Embarcadero) +* ``__is_nothrow_assignable`` (C++, MSVC 2013) +* ``__is_nothrow_constructible`` (C++, MSVC 2013) +* ``__is_nothrow_destructible`` (C++, MSVC 2013) + Only available in ``-fms-extensions`` mode. +* ``__is_object`` (C++, Embarcadero) +* ``__is_pod`` (C++, GNU, Microsoft, Embarcadero): + Note, the corresponding standard trait was deprecated in C++20. +* ``__is_pointer`` (C++, Embarcadero) +* ``__is_polymorphic`` (C++, GNU, Microsoft, Embarcadero) +* ``__is_reference`` (C++, Embarcadero) +* ``__is_rvalue_reference`` (C++, Embarcadero) +* ``__is_same`` (C++, Embarcadero) +* ``__is_scalar`` (C++, Embarcadero) +* ``__is_sealed`` (Microsoft): + Synonym for ``__is_final``. +* ``__is_signed`` (C++, Embarcadero): + Note that this currently returns true for enumeration types if the underlying + type is signed, and returns false for floating-point types, in violation of + the requirements for ``std::is_signed``. This behavior is likely to change in + a future version of Clang. +* ``__is_standard_layout`` (C++, GNU, Microsoft, Embarcadero) +* ``__is_trivial`` (C++, GNU, Microsoft, Embarcadero) +* ``__is_trivially_assignable`` (C++, GNU, Microsoft) +* ``__is_trivially_constructible`` (C++, GNU, Microsoft) +* ``__is_trivially_copyable`` (C++, GNU, Microsoft) +* ``__is_trivially_destructible`` (C++, MSVC 2013) +* ``__is_union`` (C++, GNU, Microsoft, Embarcadero) +* ``__is_unsigned`` (C++, Embarcadero) + Note that this currently returns true for enumeration types if the underlying + type is unsigned, in violation of the requirements for ``std::is_unsigned``. + This behavior is likely to change in a future version of Clang. +* ``__is_void`` (C++, Embarcadero) +* ``__is_volatile`` (C++, Embarcadero) * ``__reference_binds_to_temporary(T, U)`` (Clang): Determines whether a reference of type ``T`` bound to an expression of type ``U`` would bind to a materialized temporary object. If ``T`` is not a reference type the result is false. Note this trait will also return false when the initialization of ``T`` from ``U`` is ill-formed. +* ``__underlying_type`` (C++, GNU, Microsoft) + +In addition, the following expression traits are supported: + +* ``__is_lvalue_expr(e)`` (Embarcadero): + Returns true if ``e`` is an lvalue expression. + Deprecated, use ``__is_lvalue_reference(decltype((e)))`` instead. +* ``__is_rvalue_expr(e)`` (Embarcadero): + Returns true if ``e`` is a prvalue expression. + Deprecated, use ``!__is_reference(decltype((e)))`` instead. + +There are multiple ways to detect support for a type trait ``__X`` in the +compiler, depending on the oldest version of Clang you wish to support. + +* From Clang 10 onwards, ``__has_builtin(__X)`` can be used. +* From Clang 6 onwards, ``!__is_identifier(__X)`` can be used. +* From Clang 3 onwards, ``__has_feature(X)`` can be used, but only supports + the following traits: + + * ``__has_nothrow_assign`` + * ``__has_nothrow_copy`` + * ``__has_nothrow_constructor`` + * ``__has_trivial_assign`` + * ``__has_trivial_copy`` + * ``__has_trivial_constructor`` + * ``__has_trivial_destructor`` + * ``__has_virtual_destructor`` + * ``__is_abstract`` + * ``__is_base_of`` + * ``__is_class`` + * ``__is_constructible`` + * ``__is_convertible_to`` + * ``__is_empty`` + * ``__is_enum`` + * ``__is_final`` + * ``__is_literal`` + * ``__is_standard_layout`` + * ``__is_pod`` + * ``__is_polymorphic`` + * ``__is_sealed`` + * ``__is_trivial`` + * ``__is_trivially_assignable`` + * ``__is_trivially_constructible`` + * ``__is_trivially_copyable`` + * ``__is_union`` + * ``__underlying_type`` + +A simplistic usage example as might be seen in standard C++ headers follows: + +.. code-block:: c++ + + #if __has_builtin(__is_convertible_to) + template<typename From, typename To> + struct is_convertible_to { + static const bool value = __is_convertible_to(From, To); + }; + #else + // Emulate type trait for compatibility with other compilers. + #endif Blocks ======
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits