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
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to