diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -1646,6 +1646,20 @@ // Emulate type trait for compatibility with other compilers. #endif +.. cpp:function:: __type_pack_index(T, Ts...) + + :cpp:expr:`__type_pack_index` performs a linear search on :cpp:expr:`Ts...` for + :cpp:expr:`T` and returns its zero-based index. This is useful for implementing + functions like :cpp:expr:`std::get(tuple)`. + + .. code-block:: cpp + :caption: Example + + template + T& get(tuple& ts) noexcept { + return std::get<__type_pack_index(T, Ts...)>(ts); + } + Blocks ====== diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -2752,7 +2752,7 @@ TypeTraitExpr(QualType T, SourceLocation Loc, TypeTrait Kind, ArrayRef Args, SourceLocation RParenLoc, - bool Value); + unsigned Value); TypeTraitExpr(EmptyShell Empty) : Expr(TypeTraitExprClass, Empty) {} @@ -2770,7 +2770,7 @@ SourceLocation Loc, TypeTrait Kind, ArrayRef Args, SourceLocation RParenLoc, - bool Value); + unsigned Value); static TypeTraitExpr *CreateDeserialized(const ASTContext &C, unsigned NumArgs); @@ -2780,7 +2780,7 @@ return static_cast(TypeTraitExprBits.Kind); } - bool getValue() const { + unsigned getValue() const { assert(!isValueDependent()); return TypeTraitExprBits.Value; } diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -793,14 +793,13 @@ /// The kind of type trait, which is a value of a TypeTrait enumerator. unsigned Kind : 8; - /// If this expression is not value-dependent, this indicates whether - /// the trait evaluated true or false. - unsigned Value : 1; + /// If this expression is not value-dependent, this stores the value. + unsigned Value : 8; /// The number of arguments to this type trait. According to [implimits] /// 8 bits would be enough, but we require (and test for) at least 16 bits /// to mirror FunctionType. - unsigned NumArgs; + unsigned NumArgs : 16; }; class DependentScopeDeclRefExprBitfields { 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 @@ -2955,6 +2955,10 @@ def err_type_pack_element_out_of_bounds : Error< "a parameter pack may not be accessed at an out of bounds index">; +// __type_pack_index +def err_type_pack_index_not_found : Error< + "'__type_pack_index' couldn't find type %0">; + // Objective-C++ def err_objc_decls_may_only_appear_in_global_scope : Error< "Objective-C declarations may only appear in global scope">; 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 @@ -528,6 +528,7 @@ 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) +TYPE_TRAIT_N(__type_pack_index, TypePackIndex, KEYCXX) // Embarcadero Expression Traits EXPRESSION_TRAIT(__is_lvalue_expr, IsLValueExpr, KEYCXX) diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -8505,8 +8505,8 @@ return std::move(Err); // According to Sema::BuildTypeTrait(), if E is value-dependent, - // Value is always false. - bool ToValue = (E->isValueDependent() ? false : E->getValue()); + // Value is always 0. + unsigned ToValue = (E->isValueDependent() ? 0 : E->getValue()); return TypeTraitExpr::Create( Importer.getToContext(), ToType, ToBeginLoc, E->getTrait(), ToArgs, diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -1750,7 +1750,7 @@ TypeTraitExpr::TypeTraitExpr(QualType T, SourceLocation Loc, TypeTrait Kind, ArrayRef Args, - SourceLocation RParenLoc, bool Value) + SourceLocation RParenLoc, unsigned Value) : Expr(TypeTraitExprClass, T, VK_PRValue, OK_Ordinary), Loc(Loc), RParenLoc(RParenLoc) { assert(Kind <= TT_Last && "invalid enum value!"); @@ -1774,7 +1774,7 @@ TypeTrait Kind, ArrayRef Args, SourceLocation RParenLoc, - bool Value) { + unsigned Value) { void *Mem = C.Allocate(totalSizeToAlloc(Args.size())); return new (Mem) TypeTraitExpr(T, Loc, Kind, Args, RParenLoc, Value); } diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -1631,7 +1631,8 @@ tok::kw___is_union, tok::kw___is_unsigned, tok::kw___is_void, - tok::kw___is_volatile)) + tok::kw___is_volatile, + tok::kw___type_pack_index)) // GNU libstdc++ 4.2 and libc++ use certain intrinsic names as the // name of struct templates, but some are keywords in GCC >= 4.3 // and Clang. Therefore, when we see the token sequence "struct diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -1127,6 +1127,7 @@ REVERTIBLE_TYPE_TRAIT(__is_unsigned); REVERTIBLE_TYPE_TRAIT(__is_void); REVERTIBLE_TYPE_TRAIT(__is_volatile); + REVERTIBLE_TYPE_TRAIT(__type_pack_index); #define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) \ REVERTIBLE_TYPE_TRAIT(RTT_JOIN(__, Trait)); #include "clang/Basic/TransformTypeTraits.def" 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 @@ -5378,9 +5378,9 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, QualType LhsT, QualType RhsT, SourceLocation KeyLoc); -static bool evaluateTypeTrait(Sema &S, TypeTrait Kind, SourceLocation KWLoc, - ArrayRef Args, - SourceLocation RParenLoc) { +static bool EvaluateBooleanTypeTrait(Sema &S, TypeTrait Kind, SourceLocation KWLoc, + ArrayRef Args, + SourceLocation RParenLoc) { if (Kind <= UTT_Last) return EvaluateUnaryTypeTrait(S, Kind, KWLoc, Args[0]->getType()); @@ -5548,6 +5548,54 @@ return true; } +static ExprResult EvaluateIntegralTypeTrait(Sema &S, TypeTrait Kind, + SourceLocation KeywordLoc, + ArrayRef Args, + SourceLocation RParenLoc, + bool IsDependent) { + QualType ResultType = S.Context.getSizeType(); + + // Start with zero in case we have a dependent context. + unsigned ResultValue = 0; + + if (IsDependent) + goto Return; + + switch (Kind) { + case clang::TT_TypePackIndex: { + // __type_pack_index requires at least two values, but we only checked for + // one. + if (Args.size() < 2) { + S.Diag(KeywordLoc, diag::err_type_trait_arity) + << /*Arity*/ 2 << /*select "or more"*/ true + << /*pluralise "argument"*/ true << Args.size() + << SourceRange(KeywordLoc); + return ExprError(); + } + + QualType Needle = Args[0]->getType(); + ArrayRef Haystack = Args.drop_front(); + auto Found = llvm::find_if(Haystack, [&Needle, &S](TypeSourceInfo *RhsT) { + return S.Context.hasSameType(Needle, RhsT->getType()); + }); + + if (Found == Args.end()) { + S.Diag(KeywordLoc, diag::err_type_pack_index_not_found) << Needle; + return ExprError(); + } + + ResultValue = std::distance(Haystack.begin(), Found); + break; + } + default: + llvm_unreachable("unhandled type trait (usualy deliberate)"); + } + +Return: + return TypeTraitExpr::Create(S.Context, ResultType, KeywordLoc, Kind, Args, + RParenLoc, ResultValue); +} + ExprResult Sema::BuildTypeTrait(TypeTrait Kind, SourceLocation KWLoc, ArrayRef Args, SourceLocation RParenLoc) { @@ -5569,9 +5617,13 @@ } } + if (Kind == clang::TT_TypePackIndex) + return EvaluateIntegralTypeTrait(*this, Kind, KWLoc, Args, RParenLoc, + Dependent); + bool Result = false; if (!Dependent) - Result = evaluateTypeTrait(*this, Kind, KWLoc, Args, RParenLoc); + Result = EvaluateBooleanTypeTrait(*this, Kind, KWLoc, Args, RParenLoc); return TypeTraitExpr::Create(Context, ResultType, KWLoc, Kind, Args, RParenLoc, Result); diff --git a/clang/test/SemaCXX/type_pack_index.cpp b/clang/test/SemaCXX/type_pack_index.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/type_pack_index.cpp @@ -0,0 +1,77 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s + +using size_t = decltype(sizeof(0)); + +static_assert(__type_pack_index(int, int) == 0, ""); +static_assert(__type_pack_index(long, long, int) == 0, ""); +static_assert(__type_pack_index(int, long, int) == 1, ""); + +// This will be used by parameter packs most of the time, so we should check they +// work too. +template +constexpr size_t mock_get() +{ + return __type_pack_index(T, Ts...); +} + +static_assert(mock_get() == 0, ""); +static_assert(mock_get() == 1, ""); +static_assert(mock_get() == 2, ""); + +// Types mentioned twice return the first occurrence +// ------------------------------------------------- +enum E {}; +enum class EC : int {}; +struct S; +union U; +static_assert(__type_pack_index(S, U, S, S) == 1, ""); +static_assert(__type_pack_index(U, int, long, EC, U, double, __int128, S, EC, U) == 3, ""); + +template +union UT; +static_assert(__type_pack_index(UT, int, long, EC, U, double, __int128, S, EC, U, UT, UT, UT) == 10, ""); + +// Qualifiers +// ---------- +static_assert(__type_pack_index(int const, int const, int) == 0, ""); +static_assert(__type_pack_index(int, int const, int) == 1, ""); + +static_assert(__type_pack_index(int volatile, int volatile, int) == 0, ""); +static_assert(__type_pack_index(int, int volatile, int) == 1, ""); + +static_assert(__type_pack_index(int const volatile, int const volatile, int) == 0, ""); +static_assert(__type_pack_index(int, int const volatile, int) == 1, ""); + +static_assert(__type_pack_index(int&, int const, int, int&) == 2, ""); +static_assert(__type_pack_index(int const&, int const, int const&) == 1, ""); +static_assert(__type_pack_index(int volatile&, int volatile, int const&, int&, int volatile&) == 3, ""); +static_assert(__type_pack_index(int const volatile&, int volatile, int const&, int&, int volatile&, int const volatile&) == 4, ""); + +static_assert(__type_pack_index(int&&, int const, int, int&, int&&, int const&&) == 3, ""); +static_assert(__type_pack_index(int const&&, int const, int const&, int const&&) == 2, ""); +static_assert(__type_pack_index(int volatile&&, int volatile, int const&, int&, int volatile&, int volatile&&) == 4, ""); +static_assert(__type_pack_index(int const volatile&&, int volatile, int const&, int&, int volatile&, int volatile&&, int const volatile&&) == 5, ""); + +static_assert(__type_pack_index(int* __restrict, int*, int * __restrict) == 1, ""); + +// Aliases +// ------- +using Int = int; +typedef long Long; +static_assert(__type_pack_index(int, Int, Long) == 0, ""); +static_assert(__type_pack_index(Int, Int, Long) == 0, ""); +static_assert(__type_pack_index(long, Int, Long) == 1, ""); +static_assert(__type_pack_index(Long, Int, Long) == 1, ""); + +// Error handling +// -------------- +// +// Not enough arguments +// -------------------- +static_assert(__type_pack_index(), ""); // expected-error{{expected a type}} +static_assert(__type_pack_index(int), ""); // expected-error{{type trait requires 2 or more arguments; have 1 argument}} + +// Type not found +// -------------- +static_assert(__type_pack_index(int, long long, double)); +// expected-error@-1{{'__type_pack_index' couldn't find type 'int'}}