r316518 - mplement __has_unique_object_representations

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Thu Oct 26 09:50:02 PDT 2017


On 24 Oct 2017 14:32, "Erich Keane via cfe-commits" <
cfe-commits at lists.llvm.org> wrote:

Author: erichkeane
Date: Tue Oct 24 14:31:50 2017
New Revision: 316518

URL: http://llvm.org/viewvc/llvm-project?rev=316518&view=rev
Log:
mplement __has_unique_object_representations

A helper builtin to facilitate implementing the
std::has_unique_object_representations type trait.

Requested here: https://bugs.llvm.org/show_bug.cgi?id=34942
Also already exists in GCC and MSVC.

Differential Revision: https://reviews.llvm.org/D39064

Modified:
    cfe/trunk/include/clang/AST/Type.h
    cfe/trunk/include/clang/Basic/TokenKinds.def
    cfe/trunk/include/clang/Basic/TypeTraits.h
    cfe/trunk/lib/AST/Type.cpp
    cfe/trunk/lib/Parse/ParseExpr.cpp
    cfe/trunk/lib/Sema/SemaExprCXX.cpp
    cfe/trunk/test/SemaCXX/type-traits.cpp

Modified: cfe/trunk/include/clang/AST/Type.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/
clang/AST/Type.h?rev=316518&r1=316517&r2=316518&view=diff
============================================================
==================
--- cfe/trunk/include/clang/AST/Type.h (original)
+++ cfe/trunk/include/clang/AST/Type.h Tue Oct 24 14:31:50 2017
@@ -770,6 +770,10 @@ public:
   /// Return true if this is a trivially copyable type (C++0x
[basic.types]p9)
   bool isTriviallyCopyableType(const ASTContext &Context) const;

+  /// Return true if this has unique object representations according to
(C++17
+  /// [meta.unary.prop]p9)
+  bool hasUniqueObjectRepresentations(const ASTContext &Context) const;


I think this would make more sense as a member of ASTContext. The Type
object generally doesn't know or care about its representation.

+
   // Don't promise in the API that anything besides 'const' can be
   // easily added.

@@ -1114,6 +1118,8 @@ public:
   QualType getAtomicUnqualifiedType() const;

 private:
+  bool unionHasUniqueObjectRepresentations(const ASTContext& Context)
const;
+  bool structHasUniqueObjectRepresentations(const ASTContext& Context)
const;
   // These methods are implemented in a separate translation unit;
   // "static"-ize them to avoid creating temporary QualTypes in the
   // caller.

Modified: cfe/trunk/include/clang/Basic/TokenKinds.def
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/
clang/Basic/TokenKinds.def?rev=316518&r1=316517&r2=316518&view=diff
============================================================
==================
--- cfe/trunk/include/clang/Basic/TokenKinds.def (original)
+++ cfe/trunk/include/clang/Basic/TokenKinds.def Tue Oct 24 14:31:50 2017
@@ -455,6 +455,8 @@ TYPE_TRAIT_1(__is_pod, IsPOD, KEYCXX)
 TYPE_TRAIT_1(__is_polymorphic, IsPolymorphic, KEYCXX)
 TYPE_TRAIT_1(__is_trivial, IsTrivial, KEYCXX)
 TYPE_TRAIT_1(__is_union, IsUnion, KEYCXX)
+TYPE_TRAIT_1(__has_unique_object_representations,
+             HasUniqueObjectRepresentations, KEYCXX)

 // Clang-only C++ Type Traits
 TYPE_TRAIT_N(__is_trivially_constructible, IsTriviallyConstructible,
KEYCXX)

Modified: cfe/trunk/include/clang/Basic/TypeTraits.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/
clang/Basic/TypeTraits.h?rev=316518&r1=316517&r2=316518&view=diff
============================================================
==================
--- cfe/trunk/include/clang/Basic/TypeTraits.h (original)
+++ cfe/trunk/include/clang/Basic/TypeTraits.h Tue Oct 24 14:31:50 2017
@@ -70,7 +70,8 @@ namespace clang {
     UTT_IsUnsigned,
     UTT_IsVoid,
     UTT_IsVolatile,
-    UTT_Last = UTT_IsVolatile,
+    UTT_HasUniqueObjectRepresentations,
+    UTT_Last = UTT_HasUniqueObjectRepresentations,
     BTT_IsBaseOf,
     BTT_IsConvertible,
     BTT_IsConvertibleTo,

Modified: cfe/trunk/lib/AST/Type.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/
Type.cpp?rev=316518&r1=316517&r2=316518&view=diff
============================================================
==================
--- cfe/trunk/lib/AST/Type.cpp (original)
+++ cfe/trunk/lib/AST/Type.cpp Tue Oct 24 14:31:50 2017
@@ -2166,6 +2166,152 @@ bool QualType::isTriviallyCopyableType(c
   return false;
 }

+bool QualType::unionHasUniqueObjectRepresentations(
+    const ASTContext &Context) const {
+  assert((*this)->isUnionType() && "must be union type");
+  CharUnits UnionSize = Context.getTypeSizeInChars(*this);
+  const RecordDecl *Union = getTypePtr()->getAs<RecordType>()->getDecl();
+
+  for (const auto *Field : Union->fields()) {
+    if (!Field->getType().hasUniqueObjectRepresentations(Context))
+      return false;
+    CharUnits FieldSize = Context.getTypeSizeInChars(Field->getType());
+    if (FieldSize != UnionSize)
+      return false;
+  }
+  return true;
+}
+
+bool isStructEmpty(QualType Ty) {
+  assert(Ty.getTypePtr()->isStructureOrClassType() &&
+         "Must be struct or class");
+  const RecordDecl *RD = Ty.getTypePtr()->getAs<RecordType>()->getDecl();
+
+  if (!RD->field_empty())
+    return false;
+
+  if (const CXXRecordDecl *ClassDecl = dyn_cast<CXXRecordDecl>(RD)) {
+    return ClassDecl->isEmpty();
+  }
+
+  return true;
+}
+
+bool QualType::structHasUniqueObjectRepresentations(
+    const ASTContext &Context) const {
+  assert((*this)->isStructureOrClassType() && "Must be struct or class");
+  const RecordDecl *RD = getTypePtr()->getAs<RecordType>()->getDecl();
+
+  if (isStructEmpty(*this))
+    return false;
+
+  // Check base types.
+  CharUnits BaseSize{};
+  if (const CXXRecordDecl *ClassDecl = dyn_cast<CXXRecordDecl>(RD)) {
+    for (const auto Base : ClassDecl->bases()) {
+      if (Base.isVirtual())
+        return false;


Hmm, are there any cases in which we might want to guarantee the vptr is
identical across all instances?

+
+      // Empty bases are permitted, otherwise ensure base has unique
+      // representation. Also, Empty Base Optimization means that an
+      // Empty base takes up 0 size.


This seems really fragile to me. Empty bases may or may not occupy storage.
But that's beside the point -- empty bases are just a special case of bases
with tail padding, which are not properly handled here.

I really think you should be walking the record layout rather than trying
to determine this from sizes alone.

+      if (!isStructEmpty(Base.getType())) {
+        if (!Base.getType().structHasUniqueObjectRepresentations(Context))
+          return false;
+        BaseSize += Context.getTypeSizeInChars(Base.getType());
+      }
+    }
+  }
+
+  CharUnits StructSize = Context.getTypeSizeInChars(*this);
+
+  // This struct obviously has bases that keep it from being 'empty', so
+  // checking fields is no longer required.  Ensure that the struct size
+  // is the sum of the bases.
+  if (RD->field_empty())
+    return StructSize == BaseSize;
+  ;


Stray semicolon?

+
+  CharUnits CurOffset =
+      Context.toCharUnitsFromBits(Context.getFieldOffset(*RD->
field_begin()));
+
+  // If the first field isn't at the sum of the size of the bases, there
+  // is padding somewhere.
+  if (BaseSize != CurOffset)
+    return false;
+
+  for (const auto *Field : RD->fields()) {
+    if (!Field->getType().hasUniqueObjectRepresentations(Context))
+      return false;
+    CharUnits FieldSize = Context.getTypeSizeInChars(Field->getType());
+    CharUnits FieldOffset =
+        Context.toCharUnitsFromBits(Context.getFieldOffset(Field));
+    // Has padding between fields.
+    if (FieldOffset != CurOffset)
+      return false;
+    CurOffset += FieldSize;
+  }
+  // Check for tail padding.
+  return CurOffset == StructSize;
+}
+
+bool QualType::hasUniqueObjectRepresentations(const ASTContext &Context)
const {
+  // C++17 [meta.unary.prop]:
+  //   The predicate condition for a template specialization
+  //   has_unique_object_representations<T> shall be
+  //   satisfied if and only if:
+  //     (9.1) — T is trivially copyable, and
+  //     (9.2) — any two objects of type T with the same value have the
same
+  //     object representation, where two objects
+  //   of array or non-union class type are considered to have the same
value
+  //   if their respective sequences of
+  //   direct subobjects have the same values, and two objects of union
type
+  //   are considered to have the same
+  //   value if they have the same active member and the corresponding
members
+  //   have the same value.
+  //   The set of scalar types for which this condition holds is
+  //   implementation-defined. [ Note: If a type has padding
+  //   bits, the condition does not hold; otherwise, the condition holds
true
+  //   for unsigned integral types. — end
+  //   note ]
+  if (isNull())
+    return false;
+
+  // Arrays are unique only if their element type is unique.
+  if ((*this)->isArrayType())
+    return Context.getBaseElementType(*this).hasUniqueObjectRepresentations
(
+        Context);


I don't think this is correct. As a weird GNU behaviour, we can have, say,
a type with size 3 and alignment 4 (via an alignment attribute on a
typedef). An array of 1 such element has size 4, and has padding even if
its element type does not.

+
+  // (9.1) — T is trivially copyable, and
+  if (!isTriviallyCopyableType(Context))
+    return false;
+
+  // Functions are not unique.
+  if ((*this)->isFunctionType())
+    return false;
+
+  // All integrals and enums are unique!
+  if ((*this)->isIntegralOrEnumerationType())
+    return true;
+
+  // All pointers are unique, since they're just integrals.


The second half of this comment doesn't seem right to me. They may be
represented as sequences of bits, but that doesn't make them integral.

+  if ((*this)->isPointerType() || (*this)->isMemberPointerType())
+    return true;
+
+  if ((*this)->isRecordType()) {
+    const RecordDecl *Record = getTypePtr()->getAs<
RecordType>()->getDecl();
+
+    // Lambda types are not unique, so exclude them immediately.


Why?

+    if (Record->isLambda())
+      return false;
+
+    if (Record->isUnion())
+      return unionHasUniqueObjectRepresentations(Context);
+    return structHasUniqueObjectRepresentations(Context);


Making these members of QualType seems counterproductive. You already have
the Record here; it'd be better to make these file-static and pass that in.

+  }
+  return false;
+}
+
 bool QualType::isNonWeakInMRRWithObjCWeak(const ASTContext &Context) const
{
   return !Context.getLangOpts().ObjCAutoRefCount &&
          Context.getLangOpts().ObjCWeak &&

Modified: cfe/trunk/lib/Parse/ParseExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/
ParseExpr.cpp?rev=316518&r1=316517&r2=316518&view=diff
============================================================
==================
--- cfe/trunk/lib/Parse/ParseExpr.cpp (original)
+++ cfe/trunk/lib/Parse/ParseExpr.cpp Tue Oct 24 14:31:50 2017
@@ -716,6 +716,7 @@ class CastExpressionIdValidator : public
 ///                   '__is_sealed'                           [MS]
 ///                   '__is_trivial'
 ///                   '__is_union'
+///                   '__has_unique_object_representations'
 ///
 /// [Clang] unary-type-trait:
 ///                   '__is_aggregate'

Modified: cfe/trunk/lib/Sema/SemaExprCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/
SemaExprCXX.cpp?rev=316518&r1=316517&r2=316518&view=diff
============================================================
==================
--- cfe/trunk/lib/Sema/SemaExprCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExprCXX.cpp Tue Oct 24 14:31:50 2017
@@ -4175,6 +4175,7 @@ static bool CheckUnaryTypeTraitTypeCompl
   case UTT_IsDestructible:
   case UTT_IsNothrowDestructible:
   case UTT_IsTriviallyDestructible:
+  case UTT_HasUniqueObjectRepresentations:
     if (ArgTy->isIncompleteArrayType() || ArgTy->isVoidType())
       return true;

@@ -4614,6 +4615,8 @@ static bool EvaluateUnaryTypeTrait(Sema
     //   Returns True if and only if T is a complete type at the point of
the
     //   function call.
     return !T->isIncompleteType();
+  case UTT_HasUniqueObjectRepresentations:
+    return T.hasUniqueObjectRepresentations(C);
   }
 }


Modified: cfe/trunk/test/SemaCXX/type-traits.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/
SemaCXX/type-traits.cpp?rev=316518&r1=316517&r2=316518&view=diff
============================================================
==================
--- cfe/trunk/test/SemaCXX/type-traits.cpp (original)
+++ cfe/trunk/test/SemaCXX/type-traits.cpp Tue Oct 24 14:31:50 2017
@@ -2352,3 +2352,236 @@ void is_trivially_destructible_test() {
   { int arr[F(__is_trivially_destructible(void))]; }
   { int arr[F(__is_trivially_destructible(const volatile void))]; }
 }
+
+// Instantiation of __has_unique_object_representations
+template <typename T>
+struct has_unique_object_representations {
+  static const bool value = __has_unique_object_representations(T);
+};
+
+static_assert(!has_unique_object_representations<void>::value, "void is
never unique");
+static_assert(!has_unique_object_representations<const void>::value, "void
is never unique");
+static_assert(!has_unique_object_representations<volatile void>::value,
"void is never unique");
+static_assert(!has_unique_object_representations<const volatile
void>::value, "void is never unique");
+
+static_assert(has_unique_object_representations<int>::value, "integrals
are");
+static_assert(has_unique_object_representations<const int>::value,
"integrals are");
+static_assert(has_unique_object_representations<volatile int>::value,
"integrals are");
+static_assert(has_unique_object_representations<const volatile
int>::value, "integrals are");
+
+static_assert(has_unique_object_representations<void *>::value, "as are
pointers");
+static_assert(has_unique_object_representations<const void *>::value, "as
are pointers");
+static_assert(has_unique_object_representations<volatile void *>::value,
"are pointers");
+static_assert(has_unique_object_representations<const volatile void
*>::value, "as are pointers");
+
+static_assert(has_unique_object_representations<int *>::value, "as are
pointers");
+static_assert(has_unique_object_representations<const int *>::value, "as
are pointers");
+static_assert(has_unique_object_representations<volatile int *>::value,
"as are pointers");
+static_assert(has_unique_object_representations<const volatile int
*>::value, "as are pointers");
+
+class C {};
+using FP = int (*)(int);
+using PMF = int (C::*)(int);
+using PMD = int C::*;
+
+static_assert(has_unique_object_representations<FP>::value, "even function
pointers");
+static_assert(has_unique_object_representations<const FP>::value, "even
function pointers");
+static_assert(has_unique_object_representations<volatile FP>::value, "even
function pointers");
+static_assert(has_unique_object_representations<const volatile FP>::value,
"even function pointers");
+
+static_assert(has_unique_object_representations<PMF>::value, "and pointer
to members");
+static_assert(has_unique_object_representations<const PMF>::value, "and
pointer to members");
+static_assert(has_unique_object_representations<volatile PMF>::value, "and
pointer to members");
+static_assert(has_unique_object_representations<const volatile
PMF>::value, "and pointer to members");
+
+static_assert(has_unique_object_representations<PMD>::value, "and pointer
to members");
+static_assert(has_unique_object_representations<const PMD>::value, "and
pointer to members");
+static_assert(has_unique_object_representations<volatile PMD>::value, "and
pointer to members");
+static_assert(has_unique_object_representations<const volatile
PMD>::value, "and pointer to members");
+
+static_assert(has_unique_object_representations<bool>::value, "yes, all
integral types");
+static_assert(has_unique_object_representations<char>::value, "yes, all
integral types");
+static_assert(has_unique_object_representations<signed char>::value, "yes,
all integral types");
+static_assert(has_unique_object_representations<unsigned char>::value,
"yes, all integral types");
+static_assert(has_unique_object_representations<short>::value, "yes, all
integral types");
+static_assert(has_unique_object_representations<unsigned short>::value,
"yes, all integral types");
+static_assert(has_unique_object_representations<int>::value, "yes, all
integral types");
+static_assert(has_unique_object_representations<unsigned int>::value,
"yes, all integral types");
+static_assert(has_unique_object_representations<long>::value, "yes, all
integral types");
+static_assert(has_unique_object_representations<unsigned long>::value,
"yes, all integral types");
+static_assert(has_unique_object_representations<long long>::value, "yes,
all integral types");
+static_assert(has_unique_object_representations<unsigned long
long>::value, "yes, all integral types");
+static_assert(has_unique_object_representations<wchar_t>::value, "yes, all
integral types");
+static_assert(has_unique_object_representations<char16_t>::value, "yes,
all integral types");
+static_assert(has_unique_object_representations<char32_t>::value, "yes,
all integral types");
+
+static_assert(!has_unique_object_representations<void>::value, "but not
void!");
+static_assert(!has_unique_object_representations<decltype(nullptr)>::value,
"or nullptr_t");
+static_assert(!has_unique_object_representations<float>::value,
"definitely not Floating Point");
+static_assert(!has_unique_object_representations<double>::value,
"definitely not Floating Point");
+static_assert(!has_unique_object_representations<long double>::value,
"definitely not Floating Point");
+
+struct NoPadding {
+  int a;
+  int b;
+};
+
+static_assert(has_unique_object_representations<NoPadding>::value, "types
without padding are");
+
+struct InheritsFromNoPadding : NoPadding {
+  int c;
+  int d;
+};
+
+static_assert(has_unique_object_representations<InheritsFromNoPadding>::value,
"types without padding are");
+
+struct VirtuallyInheritsFromNoPadding : virtual NoPadding {
+  int c;
+  int d;
+};
+
+static_assert(!has_unique_object_representations<
VirtuallyInheritsFromNoPadding>::value, "No virtual inheritence");
+
+struct Padding {
+  char a;
+  int b;
+};
+
+static_assert(!has_unique_object_representations<Padding>::value, "but not
with padding");
+
+struct InheritsFromPadding : Padding {
+  int c;
+  int d;
+};
+
+static_assert(!has_unique_object_representations<InheritsFromPadding>::value,
"or its subclasses");
+
+struct TailPadding {
+  int a;
+  char b;
+};
+
+static_assert(!has_unique_object_representations<TailPadding>::value,
"even at the end");
+
+struct TinyStruct {
+  char a;
+};
+
+static_assert(has_unique_object_representations<TinyStruct>::value,
"Should be no padding");
+
+struct InheritsFromTinyStruct : TinyStruct {
+  int b;
+};
+
+static_assert(!has_unique_object_representations<InheritsFromTinyStruct>::value,
"Inherit causes padding");
+
+union NoPaddingUnion {
+  int a;
+  unsigned int b;
+};
+
+static_assert(has_unique_object_representations<NoPaddingUnion>::value,
"unions follow the same rules as structs");
+
+union PaddingUnion {
+  int a;
+  long long b;
+};
+
+static_assert(!has_unique_object_representations<PaddingUnion>::value,
"unions follow the same rules as structs");
+
+struct NotTriviallyCopyable {
+  int x;
+  NotTriviallyCopyable(const NotTriviallyCopyable &) {}
+};
+
+static_assert(!has_unique_object_representations<NotTriviallyCopyable>::value,
"must be trivially copyable");
+
+struct HasNonUniqueMember {
+  float x;
+};
+
+static_assert(!has_unique_object_representations<HasNonUniqueMember>::value,
"all members must be unique");
+
+enum ExampleEnum { xExample,
+                   yExample };
+enum LLEnum : long long { xLongExample,
+                          yLongExample };
+
+static_assert(has_unique_object_representations<ExampleEnum>::value,
"Enums are integrals, so unique!");
+static_assert(has_unique_object_representations<LLEnum>::value, "Enums are
integrals, so unique!");
+
+enum class ExampleEnumClass { xExample,
+                              yExample };
+enum class LLEnumClass : long long { xLongExample,
+                                     yLongExample };
+
+static_assert(has_unique_object_representations<ExampleEnumClass>::value,
"Enums are integrals, so unique!");
+static_assert(has_unique_object_representations<LLEnumClass>::value,
"Enums are integrals, so unique!");
+
+// because reference types aren't object types
+static_assert(!has_unique_object_representations<int &>::value, "No
references!");
+static_assert(!has_unique_object_representations<const int &>::value, "No
references!");
+static_assert(!has_unique_object_representations<volatile int &>::value,
"No references!");
+static_assert(!has_unique_object_representations<const volatile int
&>::value, "No references!");
+
+static_assert(!has_unique_object_representations<Empty>::value, "No empty
types!");
+
+class Compressed : Empty {
+  int x;
+};
+
+static_assert(has_unique_object_representations<Compressed>::value, "But
inheriting from one is ok");
+
+class EmptyInheritor : Compressed {};
+
+static_assert(has_unique_object_representations<EmptyInheritor>::value,
"As long as the base has items, empty is ok");
+
+class Dynamic {
+  virtual void A();
+  int i;
+};
+
+static_assert(!has_unique_object_representations<Dynamic>::value, "Dynamic
types are not valid");
+
+class InheritsDynamic : Dynamic {
+  int j;
+};
+
+static_assert(!has_unique_object_representations<InheritsDynamic>::value,
"Dynamic types are not valid");
+
+static_assert(has_unique_object_representations<int[42]>::value, "Arrays
are fine, as long as their value type is");
+static_assert(has_unique_object_representations<int[]>::value, "Arrays are
fine, as long as their value type is");
+static_assert(has_unique_object_representations<int[][42]>::value, "Arrays
are fine, as long as their value type is");
+static_assert(!has_unique_object_representations<double[42]>::value, "So
no array of doubles!");
+static_assert(!has_unique_object_representations<double[]>::value, "So no
array of doubles!");
+static_assert(!has_unique_object_representations<double[][42]>::value, "So
no array of doubles!");
+
+static_assert(!has_unique_object_representations<int(int)>::value,
"Functions are not unique");
+static_assert(!has_unique_object_representations<int(int) const>::value,
"Functions are not unique");
+static_assert(!has_unique_object_representations<int(int)
volatile>::value, "Functions are not unique");
+static_assert(!has_unique_object_representations<int(int) const
volatile>::value, "Functions are not unique");
+static_assert(!has_unique_object_representations<int(int) &>::value,
"Functions are not unique");
+static_assert(!has_unique_object_representations<int(int) const &>::value,
"Functions are not unique");
+static_assert(!has_unique_object_representations<int(int) volatile
&>::value, "Functions are not unique");
+static_assert(!has_unique_object_representations<int(int) const volatile
&>::value, "Functions are not unique");
+static_assert(!has_unique_object_representations<int(int) &&>::value,
"Functions are not unique");
+static_assert(!has_unique_object_representations<int(int) const
&&>::value, "Functions are not unique");
+static_assert(!has_unique_object_representations<int(int) volatile
&&>::value, "Functions are not unique");
+static_assert(!has_unique_object_representations<int(int) const volatile
&&>::value, "Functions are not unique");
+
+static_assert(!has_unique_object_representations<int(int, ...)>::value,
"Functions are not unique");
+static_assert(!has_unique_object_representations<int(int, ...)
const>::value, "Functions are not unique");
+static_assert(!has_unique_object_representations<int(int, ...)
volatile>::value, "Functions are not unique");
+static_assert(!has_unique_object_representations<int(int, ...) const
volatile>::value, "Functions are not unique");
+static_assert(!has_unique_object_representations<int(int, ...) &>::value,
"Functions are not unique");
+static_assert(!has_unique_object_representations<int(int, ...) const
&>::value, "Functions are not unique");
+static_assert(!has_unique_object_representations<int(int, ...) volatile
&>::value, "Functions are not unique");
+static_assert(!has_unique_object_representations<int(int, ...) const
volatile &>::value, "Functions are not unique");
+static_assert(!has_unique_object_representations<int(int, ...) &&>::value,
"Functions are not unique");
+static_assert(!has_unique_object_representations<int(int, ...) const
&&>::value, "Functions are not unique");
+static_assert(!has_unique_object_representations<int(int, ...) volatile
&&>::value, "Functions are not unique");
+static_assert(!has_unique_object_representations<int(int, ...) const
volatile &&>::value, "Functions are not unique");
+
+static auto lambda = []() {};
+static_assert(!has_unique_object_representations<decltype(lambda)>::value,
"Lambdas are not unique");
+


_______________________________________________
cfe-commits mailing list
cfe-commits at lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20171026/e8baff94/attachment-0001.html>


More information about the cfe-commits mailing list