cjdb created this revision.
cjdb added reviewers: aaron.ballman, erichkeane, shafik, dblaikie.
Herald added subscribers: arphaman, martong.
Herald added a project: All.
cjdb requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.
Similarly to `__type_pack_element`, C++ doesn't offer a way to extract a
the index of the first occurrence of a type in a parameter pack. This
means that we need to build up context for something that the compiler
already has, and then compute the value in a much less efficient manner
than the compiler.
Clang can compute this information without needing to use extra
resources.
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D151952
Files:
clang/docs/LanguageExtensions.rst
clang/include/clang/AST/ExprCXX.h
clang/include/clang/AST/Stmt.h
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Basic/TokenKinds.def
clang/lib/AST/ASTImporter.cpp
clang/lib/AST/ExprCXX.cpp
clang/lib/Parse/ParseDeclCXX.cpp
clang/lib/Parse/ParseExpr.cpp
clang/lib/Sema/SemaExprCXX.cpp
clang/test/SemaCXX/type_pack_index.cpp
Index: clang/test/SemaCXX/type_pack_index.cpp
===================================================================
--- /dev/null
+++ 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<class T, size_t N, class... Ts>
+constexpr size_t mock_get()
+{
+ return __type_pack_index(T, Ts...);
+}
+
+static_assert(mock_get<int, 0, int, long, double>() == 0, "");
+static_assert(mock_get<long, 1, float, long, int>() == 1, "");
+static_assert(mock_get<double, 2, struct S, long, double>() == 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<class T>
+union UT;
+static_assert(__type_pack_index(UT<double>, int, long, EC, U, double, __int128, S, EC, U, UT<int>, UT<double>, UT<short>) == 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'}}
Index: clang/lib/Sema/SemaExprCXX.cpp
===================================================================
--- clang/lib/Sema/SemaExprCXX.cpp
+++ clang/lib/Sema/SemaExprCXX.cpp
@@ -5378,9 +5378,10 @@
static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, QualType LhsT,
QualType RhsT, SourceLocation KeyLoc);
-static bool evaluateTypeTrait(Sema &S, TypeTrait Kind, SourceLocation KWLoc,
- ArrayRef<TypeSourceInfo *> Args,
- SourceLocation RParenLoc) {
+static bool EvaluateBooleanTypeTrait(Sema &S, TypeTrait Kind,
+ SourceLocation KWLoc,
+ ArrayRef<TypeSourceInfo *> Args,
+ SourceLocation RParenLoc) {
if (Kind <= UTT_Last)
return EvaluateUnaryTypeTrait(S, Kind, KWLoc, Args[0]->getType());
@@ -5548,6 +5549,56 @@
return true;
}
+static ExprResult EvaluateIntegralTypeTrait(Sema &S, TypeTrait Kind,
+ SourceLocation KeywordLoc,
+ ArrayRef<TypeSourceInfo *> 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();
+ }
+
+ const TypeSourceInfo *Needle = Args[0];
+ auto Found =
+ std::find_if(Args.begin() + 1, Args.end(),
+ [LhsT = Needle->getType(), &S](TypeSourceInfo *RhsT) {
+ return S.Context.hasSameType(LhsT, RhsT->getType());
+ });
+
+ if (Found == Args.end()) {
+ S.Diag(KeywordLoc, diag::err_type_pack_index_not_found)
+ << Needle->getType();
+ return ExprError();
+ }
+
+ ResultValue = std::distance(Args.begin() + 1, 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<TypeSourceInfo *> Args,
SourceLocation RParenLoc) {
@@ -5569,9 +5620,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);
Index: clang/lib/Parse/ParseExpr.cpp
===================================================================
--- clang/lib/Parse/ParseExpr.cpp
+++ 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"
Index: clang/lib/Parse/ParseDeclCXX.cpp
===================================================================
--- clang/lib/Parse/ParseDeclCXX.cpp
+++ 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
Index: clang/lib/AST/ExprCXX.cpp
===================================================================
--- clang/lib/AST/ExprCXX.cpp
+++ clang/lib/AST/ExprCXX.cpp
@@ -1750,7 +1750,7 @@
TypeTraitExpr::TypeTraitExpr(QualType T, SourceLocation Loc, TypeTrait Kind,
ArrayRef<TypeSourceInfo *> 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<TypeSourceInfo *> Args,
SourceLocation RParenLoc,
- bool Value) {
+ unsigned Value) {
void *Mem = C.Allocate(totalSizeToAlloc<TypeSourceInfo *>(Args.size()));
return new (Mem) TypeTraitExpr(T, Loc, Kind, Args, RParenLoc, Value);
}
Index: clang/lib/AST/ASTImporter.cpp
===================================================================
--- clang/lib/AST/ASTImporter.cpp
+++ 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,
Index: clang/include/clang/Basic/TokenKinds.def
===================================================================
--- clang/include/clang/Basic/TokenKinds.def
+++ 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)
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ 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">;
Index: clang/include/clang/AST/Stmt.h
===================================================================
--- clang/include/clang/AST/Stmt.h
+++ 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 {
Index: clang/include/clang/AST/ExprCXX.h
===================================================================
--- clang/include/clang/AST/ExprCXX.h
+++ clang/include/clang/AST/ExprCXX.h
@@ -2752,7 +2752,7 @@
TypeTraitExpr(QualType T, SourceLocation Loc, TypeTrait Kind,
ArrayRef<TypeSourceInfo *> Args,
SourceLocation RParenLoc,
- bool Value);
+ unsigned Value);
TypeTraitExpr(EmptyShell Empty) : Expr(TypeTraitExprClass, Empty) {}
@@ -2770,7 +2770,7 @@
SourceLocation Loc, TypeTrait Kind,
ArrayRef<TypeSourceInfo *> Args,
SourceLocation RParenLoc,
- bool Value);
+ unsigned Value);
static TypeTraitExpr *CreateDeserialized(const ASTContext &C,
unsigned NumArgs);
@@ -2780,7 +2780,7 @@
return static_cast<TypeTrait>(TypeTraitExprBits.Kind);
}
- bool getValue() const {
+ unsigned getValue() const {
assert(!isValueDependent());
return TypeTraitExprBits.Value;
}
Index: clang/docs/LanguageExtensions.rst
===================================================================
--- clang/docs/LanguageExtensions.rst
+++ 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<T>(tuple)`.
+
+ .. code-block:: cpp
+ :caption: Example
+
+ template<class T, class... Ts>
+ T& get(tuple<Ts...>& ts) noexcept {
+ return std::get<__type_pack_index(T, Ts...)>(ts);
+ }
+
Blocks
======
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits