r348938 - [ExprConstant] Improve memchr/memcmp for type mismatch and multibyte element types

Hubert Tong via cfe-commits cfe-commits at lists.llvm.org
Wed Dec 12 08:53:43 PST 2018


Author: hubert.reinterpretcast
Date: Wed Dec 12 08:53:43 2018
New Revision: 348938

URL: http://llvm.org/viewvc/llvm-project?rev=348938&view=rev
Log:
[ExprConstant] Improve memchr/memcmp for type mismatch and multibyte element types

Summary:
`memchr` and `memcmp` operate upon the character units of the object
representation; that is, the `size_t` parameter expresses the number of
character units. The constant folding implementation is updated in this
patch to account for multibyte element types in the arrays passed to
`memchr`/`memcmp` and, in the case of `memcmp`, to account for the
possibility that the arrays may have differing element types (even when
they are byte-sized).

Actual inspection of the object representation is not implemented.
Comparisons are done only between elements with the same object size;
that is, `memchr` will fail when inspecting at least one character unit
of a multibyte element. The integer types are assumed to have two's
complement representation with 0 for `false`, 1 for `true`, and no
padding bits.

`memcmp` on multibyte elements will only be able to fold in cases where
enough elements are equal for the answer to be 0.

Various tests are added to guard against incorrect folding for cases
that miscompile on some system or other prior to this patch. At the same
time, the unsigned 32-bit `wchar_t` testing in
`test/SemaCXX/constexpr-string.cpp` is restored.

Reviewers: rsmith, aaron.ballman, hfinkel

Reviewed By: rsmith

Subscribers: cfe-commits

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

Modified:
    cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td
    cfe/trunk/lib/AST/ExprConstant.cpp
    cfe/trunk/test/SemaCXX/constexpr-string.cpp

Modified: cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td?rev=348938&r1=348937&r2=348938&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td Wed Dec 12 08:53:43 2018
@@ -121,6 +121,8 @@ def note_constexpr_ltor_non_const_int :
   "read of non-const variable %0 is not allowed in a constant expression">;
 def note_constexpr_ltor_non_constexpr : Note<
   "read of non-constexpr variable %0 is not allowed in a constant expression">;
+def note_constexpr_ltor_incomplete_type : Note<
+  "read of incomplete type %0 is not allowed in a constant expression">;
 def note_constexpr_access_null : Note<
   "%select{read of|assignment to|increment of|decrement of}0 "
   "dereferenced null pointer is not allowed in a constant expression">;

Modified: cfe/trunk/lib/AST/ExprConstant.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprConstant.cpp?rev=348938&r1=348937&r2=348938&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ExprConstant.cpp (original)
+++ cfe/trunk/lib/AST/ExprConstant.cpp Wed Dec 12 08:53:43 2018
@@ -1290,6 +1290,14 @@ void EvalInfo::addCallStack(unsigned Lim
   }
 }
 
+/// Kinds of access we can perform on an object, for diagnostics.
+enum AccessKinds {
+  AK_Read,
+  AK_Assign,
+  AK_Increment,
+  AK_Decrement
+};
+
 namespace {
   struct ComplexValue {
   private:
@@ -1395,21 +1403,36 @@ namespace {
       set(B, true);
     }
 
+  private:
     // Check that this LValue is not based on a null pointer. If it is, produce
     // a diagnostic and mark the designator as invalid.
-    bool checkNullPointer(EvalInfo &Info, const Expr *E,
-                          CheckSubobjectKind CSK) {
+    template <typename GenDiagType>
+    bool checkNullPointerDiagnosingWith(const GenDiagType &GenDiag) {
       if (Designator.Invalid)
         return false;
       if (IsNullPtr) {
-        Info.CCEDiag(E, diag::note_constexpr_null_subobject)
-          << CSK;
+        GenDiag();
         Designator.setInvalid();
         return false;
       }
       return true;
     }
 
+  public:
+    bool checkNullPointer(EvalInfo &Info, const Expr *E,
+                          CheckSubobjectKind CSK) {
+      return checkNullPointerDiagnosingWith([&Info, E, CSK] {
+        Info.CCEDiag(E, diag::note_constexpr_null_subobject) << CSK;
+      });
+    }
+
+    bool checkNullPointerForFoldAccess(EvalInfo &Info, const Expr *E,
+                                       AccessKinds AK) {
+      return checkNullPointerDiagnosingWith([&Info, E, AK] {
+        Info.FFDiag(E, diag::note_constexpr_access_null) << AK;
+      });
+    }
+
     // Check this LValue refers to an object. If not, set the designator to be
     // invalid and emit a diagnostic.
     bool checkSubobject(EvalInfo &Info, const Expr *E, CheckSubobjectKind CSK) {
@@ -2746,14 +2769,6 @@ static bool diagnoseUnreadableFields(Eva
   return false;
 }
 
-/// Kinds of access we can perform on an object, for diagnostics.
-enum AccessKinds {
-  AK_Read,
-  AK_Assign,
-  AK_Increment,
-  AK_Decrement
-};
-
 namespace {
 /// A handle to a complete object (an object that is not a subobject of
 /// another object).
@@ -6129,9 +6144,27 @@ bool PointerExprEvaluator::VisitBuiltinC
         return false;
       MaxLength = N.getExtValue();
     }
-
-    QualType CharTy = E->getArg(0)->getType()->getPointeeType();
-
+    // We cannot find the value if there are no candidates to match against.
+    if (MaxLength == 0u)
+      return ZeroInitialization(E);
+    if (!Result.checkNullPointerForFoldAccess(Info, E, AK_Read) ||
+        Result.Designator.Invalid)
+      return false;
+    QualType CharTy = Result.Designator.getType(Info.Ctx);
+    bool IsRawByte = BuiltinOp == Builtin::BImemchr ||
+                     BuiltinOp == Builtin::BI__builtin_memchr;
+    assert(IsRawByte ||
+           Info.Ctx.hasSameUnqualifiedType(
+               CharTy, E->getArg(0)->getType()->getPointeeType()));
+    // Pointers to const void may point to objects of incomplete type.
+    if (IsRawByte && CharTy->isIncompleteType()) {
+      Info.FFDiag(E, diag::note_constexpr_ltor_incomplete_type) << CharTy;
+      return false;
+    }
+    // Give up on byte-oriented matching against multibyte elements.
+    // FIXME: We can compare the bytes in the correct order.
+    if (IsRawByte && Info.Ctx.getTypeSizeInChars(CharTy) != CharUnits::One())
+      return false;
     // Figure out what value we're actually looking for (after converting to
     // the corresponding unsigned type if necessary).
     uint64_t DesiredVal;
@@ -8385,8 +8418,6 @@ bool IntExprEvaluator::VisitBuiltinCallE
         !EvaluatePointer(E->getArg(1), String2, Info))
       return false;
 
-    QualType CharTy = E->getArg(0)->getType()->getPointeeType();
-
     uint64_t MaxLength = uint64_t(-1);
     if (BuiltinOp != Builtin::BIstrcmp &&
         BuiltinOp != Builtin::BIwcscmp &&
@@ -8397,6 +8428,88 @@ bool IntExprEvaluator::VisitBuiltinCallE
         return false;
       MaxLength = N.getExtValue();
     }
+
+    // Empty substrings compare equal by definition.
+    if (MaxLength == 0u)
+      return Success(0, E);
+
+    if (!String1.checkNullPointerForFoldAccess(Info, E, AK_Read) ||
+        !String2.checkNullPointerForFoldAccess(Info, E, AK_Read) ||
+        String1.Designator.Invalid || String2.Designator.Invalid)
+      return false;
+
+    QualType CharTy1 = String1.Designator.getType(Info.Ctx);
+    QualType CharTy2 = String2.Designator.getType(Info.Ctx);
+
+    bool IsRawByte = BuiltinOp == Builtin::BImemcmp ||
+                     BuiltinOp == Builtin::BI__builtin_memcmp;
+
+    assert(IsRawByte ||
+           (Info.Ctx.hasSameUnqualifiedType(
+                CharTy1, E->getArg(0)->getType()->getPointeeType()) &&
+            Info.Ctx.hasSameUnqualifiedType(CharTy1, CharTy2)));
+
+    const auto &ReadCurElems = [&](APValue &Char1, APValue &Char2) {
+      return handleLValueToRValueConversion(Info, E, CharTy1, String1, Char1) &&
+             handleLValueToRValueConversion(Info, E, CharTy2, String2, Char2) &&
+             Char1.isInt() && Char2.isInt();
+    };
+    const auto &AdvanceElems = [&] {
+      return HandleLValueArrayAdjustment(Info, E, String1, CharTy1, 1) &&
+             HandleLValueArrayAdjustment(Info, E, String2, CharTy2, 1);
+    };
+
+    if (IsRawByte) {
+      uint64_t BytesRemaining = MaxLength;
+      // Pointers to const void may point to objects of incomplete type.
+      if (CharTy1->isIncompleteType()) {
+        Info.FFDiag(E, diag::note_constexpr_ltor_incomplete_type) << CharTy1;
+        return false;
+      }
+      if (CharTy2->isIncompleteType()) {
+        Info.FFDiag(E, diag::note_constexpr_ltor_incomplete_type) << CharTy2;
+        return false;
+      }
+      uint64_t CharTy1Width{Info.Ctx.getTypeSize(CharTy1)};
+      CharUnits CharTy1Size = Info.Ctx.toCharUnitsFromBits(CharTy1Width);
+      // Give up on comparing between elements with disparate widths.
+      if (CharTy1Size != Info.Ctx.getTypeSizeInChars(CharTy2))
+        return false;
+      uint64_t BytesPerElement = CharTy1Size.getQuantity();
+      assert(BytesRemaining && "BytesRemaining should not be zero: the "
+                               "following loop considers at least one element");
+      while (true) {
+        APValue Char1, Char2;
+        if (!ReadCurElems(Char1, Char2))
+          return false;
+        // We have compatible in-memory widths, but a possible type and
+        // (for `bool`) internal representation mismatch.
+        // Assuming two's complement representation, including 0 for `false` and
+        // 1 for `true`, we can check an appropriate number of elements for
+        // equality even if they are not byte-sized.
+        APSInt Char1InMem = Char1.getInt().extOrTrunc(CharTy1Width);
+        APSInt Char2InMem = Char2.getInt().extOrTrunc(CharTy1Width);
+        if (Char1InMem.ne(Char2InMem)) {
+          // If the elements are byte-sized, then we can produce a three-way
+          // comparison result in a straightforward manner.
+          if (BytesPerElement == 1u) {
+            // memcmp always compares unsigned chars.
+            return Success(Char1InMem.ult(Char2InMem) ? -1 : 1, E);
+          }
+          // The result is byte-order sensitive, and we have multibyte elements.
+          // FIXME: We can compare the remaining bytes in the correct order.
+          return false;
+        }
+        if (!AdvanceElems())
+          return false;
+        if (BytesRemaining <= BytesPerElement)
+          break;
+        BytesRemaining -= BytesPerElement;
+      }
+      // Enough elements are equal to account for the memcmp limit.
+      return Success(0, E);
+    }
+
     bool StopAtNull = (BuiltinOp != Builtin::BImemcmp &&
                        BuiltinOp != Builtin::BIwmemcmp &&
                        BuiltinOp != Builtin::BI__builtin_memcmp &&
@@ -8407,11 +8520,10 @@ bool IntExprEvaluator::VisitBuiltinCallE
                   BuiltinOp == Builtin::BI__builtin_wcscmp ||
                   BuiltinOp == Builtin::BI__builtin_wcsncmp ||
                   BuiltinOp == Builtin::BI__builtin_wmemcmp;
+
     for (; MaxLength; --MaxLength) {
       APValue Char1, Char2;
-      if (!handleLValueToRValueConversion(Info, E, CharTy, String1, Char1) ||
-          !handleLValueToRValueConversion(Info, E, CharTy, String2, Char2) ||
-          !Char1.isInt() || !Char2.isInt())
+      if (!ReadCurElems(Char1, Char2))
         return false;
       if (Char1.getInt() != Char2.getInt()) {
         if (IsWide) // wmemcmp compares with wchar_t signedness.
@@ -8422,8 +8534,7 @@ bool IntExprEvaluator::VisitBuiltinCallE
       if (StopAtNull && !Char1.getInt())
         return Success(0, E);
       assert(!(StopAtNull && !Char2.getInt()));
-      if (!HandleLValueArrayAdjustment(Info, E, String1, CharTy, 1) ||
-          !HandleLValueArrayAdjustment(Info, E, String2, CharTy, 1))
+      if (!AdvanceElems())
         return false;
     }
     // We hit the strncmp / memcmp limit.

Modified: cfe/trunk/test/SemaCXX/constexpr-string.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/constexpr-string.cpp?rev=348938&r1=348937&r2=348938&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/constexpr-string.cpp (original)
+++ cfe/trunk/test/SemaCXX/constexpr-string.cpp Wed Dec 12 08:53:43 2018
@@ -1,8 +1,11 @@
-// RUN: %clang_cc1 %s -triple x86_64-linux-gnu -std=c++1z -fsyntax-only -verify -pedantic
-// RUN: %clang_cc1 %s -triple x86_64-linux-gnu -std=c++1z -fsyntax-only -verify -pedantic -fno-signed-char
-// RUN: %clang_cc1 %s -triple x86_64-linux-gnu -std=c++1z -fsyntax-only -verify -pedantic -fno-wchar -Dwchar_t=__WCHAR_TYPE__
+// RUN: %clang_cc1 %s -triple x86_64-linux-gnu -std=c++2a -fsyntax-only -verify -pedantic -Wno-vla-extension
+// RUN: %clang_cc1 %s -triple x86_64-linux-gnu -std=c++2a -fsyntax-only -verify -pedantic -Wno-vla-extension -fno-signed-char
+// RUN: %clang_cc1 %s -triple x86_64-linux-gnu -std=c++2a -fsyntax-only -verify -pedantic -Wno-vla-extension -fno-wchar -DNO_PREDEFINED_WCHAR_T
+// RUN: %clang_cc1 %s -triple armebv7-unknown-linux -std=c++2a -fsyntax-only -verify -pedantic -Wno-vla-extension
+// RUN: %clang_cc1 %s -triple armebv7-unknown-linux -std=c++2a -fsyntax-only -verify -pedantic -Wno-vla-extension -fno-signed-char
+// RUN: %clang_cc1 %s -triple armebv7-unknown-linux -std=c++2a -fsyntax-only -verify -pedantic -Wno-vla-extension -fno-wchar -DNO_PREDEFINED_WCHAR_T
 
-# 6 "/usr/include/string.h" 1 3 4
+# 9 "/usr/include/string.h" 1 3 4
 extern "C" {
   typedef decltype(sizeof(int)) size_t;
 
@@ -18,10 +21,13 @@ extern "C" {
   extern void *memcpy(void *d, const void *s, size_t n);
   extern void *memmove(void *d, const void *s, size_t n);
 }
-# 22 "SemaCXX/constexpr-string.cpp" 2
+# 25 "SemaCXX/constexpr-string.cpp" 2
 
-# 24 "/usr/include/wchar.h" 1 3 4
+# 27 "/usr/include/wchar.h" 1 3 4
 extern "C" {
+#if NO_PREDEFINED_WCHAR_T
+  typedef decltype(L'0') wchar_t;
+#endif
   extern size_t wcslen(const wchar_t *p);
 
   extern int wcscmp(const wchar_t *s1, const wchar_t *s2);
@@ -35,7 +41,7 @@ extern "C" {
   extern wchar_t *wmemmove(wchar_t *d, const wchar_t *s, size_t n);
 }
 
-# 39 "SemaCXX/constexpr-string.cpp" 2
+# 45 "SemaCXX/constexpr-string.cpp" 2
 namespace Strlen {
   constexpr int n = __builtin_strlen("hello"); // ok
   static_assert(n == 5);
@@ -95,11 +101,142 @@ namespace StrcmpEtc {
   static_assert(__builtin_memcmp("abab\0banana", "abab\0canada", 6) == -1);
   static_assert(__builtin_memcmp("abab\0banana", "abab\0canada", 5) == 0);
 
+  extern struct Incomplete incomplete;
+  static_assert(__builtin_memcmp(&incomplete, "", 0u) == 0);
+  static_assert(__builtin_memcmp("", &incomplete, 0u) == 0);
+  static_assert(__builtin_memcmp(&incomplete, "", 1u) == 42); // expected-error {{not an integral constant}} expected-note {{read of incomplete type 'struct Incomplete'}}
+  static_assert(__builtin_memcmp("", &incomplete, 1u) == 42); // expected-error {{not an integral constant}} expected-note {{read of incomplete type 'struct Incomplete'}}
+
+  constexpr unsigned char ku00fe00[] = {0x00, 0xfe, 0x00};
+  constexpr unsigned char ku00feff[] = {0x00, 0xfe, 0xff};
+  constexpr signed char ks00fe00[] = {0, -2, 0};
+  constexpr signed char ks00feff[] = {0, -2, -1};
+  static_assert(__builtin_memcmp(ku00feff, ks00fe00, 2) == 0);
+  static_assert(__builtin_memcmp(ku00feff, ks00fe00, 99) == 1);
+  static_assert(__builtin_memcmp(ku00fe00, ks00feff, 99) == -1);
+  static_assert(__builtin_memcmp(ks00feff, ku00fe00, 2) == 0);
+  static_assert(__builtin_memcmp(ks00feff, ku00fe00, 99) == 1);
+  static_assert(__builtin_memcmp(ks00fe00, ku00feff, 99) == -1);
+  static_assert(__builtin_memcmp(ks00fe00, ks00feff, 2) == 0);
+  static_assert(__builtin_memcmp(ks00feff, ks00fe00, 99) == 1);
+  static_assert(__builtin_memcmp(ks00fe00, ks00feff, 99) == -1);
+
+  struct Bool3Tuple { bool bb[3]; };
+  constexpr Bool3Tuple kb000100 = {{false, true, false}};
+  static_assert(sizeof(bool) != 1u || __builtin_memcmp(ks00fe00, kb000100.bb, 1) == 0);
+  static_assert(sizeof(bool) != 1u || __builtin_memcmp(ks00fe00, kb000100.bb, 2) == 1);
+
+  constexpr long ksl[] = {0, -1};
+  constexpr unsigned int kui[] = {0, 0u - 1};
+  constexpr unsigned long long kull[] = {0, 0ull - 1};
+  constexpr const auto *kuSizeofLong(void) {
+    if constexpr(sizeof(long) == sizeof(int)) {
+      return kui;
+    } else if constexpr(sizeof(long) == sizeof(long long)) {
+      return kull;
+    } else {
+      return nullptr;
+    }
+  }
+  static_assert(__builtin_memcmp(ksl, kuSizeofLong(), sizeof(long) - 1) == 0);
+  static_assert(__builtin_memcmp(ksl, kuSizeofLong(), sizeof(long) + 0) == 0);
+  static_assert(__builtin_memcmp(ksl, kuSizeofLong(), sizeof(long) + 1) == 0);
+  static_assert(__builtin_memcmp(ksl, kuSizeofLong(), 2*sizeof(long) - 1) == 0);
+  static_assert(__builtin_memcmp(ksl, kuSizeofLong(), 2*sizeof(long) + 0) == 0);
+  static_assert(__builtin_memcmp(ksl, kuSizeofLong(), 2*sizeof(long) + 1) == 42); // expected-error {{not an integral constant}} expected-note {{dereferenced one-past-the-end}}
+  static_assert(__builtin_memcmp(ksl + 1, kuSizeofLong() + 1, sizeof(long) - 1) == 0);
+  static_assert(__builtin_memcmp(ksl + 1, kuSizeofLong() + 1, sizeof(long) + 0) == 0);
+  static_assert(__builtin_memcmp(ksl + 1, kuSizeofLong() + 1, sizeof(long) + 1) == 42); // expected-error {{not an integral constant}} expected-note {{dereferenced one-past-the-end}}
+
   constexpr int a = strcmp("hello", "world"); // expected-error {{constant expression}} expected-note {{non-constexpr function 'strcmp' cannot be used in a constant expression}}
   constexpr int b = strncmp("hello", "world", 3); // expected-error {{constant expression}} expected-note {{non-constexpr function 'strncmp' cannot be used in a constant expression}}
   constexpr int c = memcmp("hello", "world", 3); // expected-error {{constant expression}} expected-note {{non-constexpr function 'memcmp' cannot be used in a constant expression}}
 }
 
+namespace MultibyteElementTests {
+inline namespace Util {
+#define STR2(X) #X
+#define STR(X) STR2(X)
+constexpr const char ByteOrderString[] = STR(__BYTE_ORDER__);
+#undef STR
+#undef STR2
+constexpr bool LittleEndian{*ByteOrderString == '1'};
+
+constexpr size_t GoodFoldArraySize = 42, BadFoldArraySize = 43;
+struct NotBadFoldResult {};
+template <size_t> struct FoldResult;
+template <> struct FoldResult<GoodFoldArraySize> : NotBadFoldResult {};
+template <typename T, size_t N>
+FoldResult<N> *foldResultImpl(T (*ptrToConstantSizeArray)[N]);
+struct NotFolded : NotBadFoldResult {};
+NotFolded *foldResultImpl(bool anyPtr);
+template <auto Value> struct MetaValue;
+template <typename Callable, size_t N, auto ExpectedFoldResult>
+auto foldResult(const Callable &, MetaValue<N> *,
+                MetaValue<ExpectedFoldResult> *) {
+  int (*maybeVLAPtr)[Callable{}(N) == ExpectedFoldResult
+                         ? GoodFoldArraySize
+                         : BadFoldArraySize] = 0;
+  return foldResultImpl(maybeVLAPtr);
+}
+template <typename FoldResultKind, typename Callable, typename NWrap,
+          typename ExpectedWrap>
+constexpr bool checkFoldResult(const Callable &c, NWrap *n, ExpectedWrap *e) {
+  decltype(static_cast<FoldResultKind *>(foldResult(c, n, e))) *chk{};
+  return true;
+}
+template <size_t N> constexpr MetaValue<N> *withN() { return nullptr; }
+template <auto Expected> constexpr MetaValue<Expected> *withExpected() {
+  return nullptr;
+}
+} // namespace Util
+} // namespace MultibyteElementTests
+
+namespace MultibyteElementTests::Memcmp {
+#ifdef __SIZEOF_INT128__
+constexpr __int128 i128_ff_8_00_8 = -(__int128)1 - -1ull;
+constexpr __int128 i128_00_16 = 0;
+static_assert(checkFoldResult<NotBadFoldResult>(
+    [](size_t n) constexpr {
+      return __builtin_memcmp(&i128_ff_8_00_8, &i128_00_16, n);
+    },
+    withN<1u>(), withExpected<LittleEndian ? 0 : 1>()));
+#endif
+
+constexpr const signed char ByteOrderStringReduced[] = {
+    ByteOrderString[0] - '0', ByteOrderString[1] - '0',
+    ByteOrderString[2] - '0', ByteOrderString[3] - '0',
+};
+constexpr signed int i04030201 = 0x04030201;
+constexpr unsigned int u04030201 = 0x04030201u;
+static_assert(checkFoldResult<NotBadFoldResult>(
+    [](size_t n) constexpr {
+      return __builtin_memcmp(ByteOrderStringReduced, &i04030201, n);
+    },
+    withN<sizeof(int)>(), withExpected<0>()));
+static_assert(checkFoldResult<NotBadFoldResult>(
+    [](size_t n) constexpr {
+      return __builtin_memcmp(&u04030201, ByteOrderStringReduced, n);
+    },
+    withN<sizeof(int)>(), withExpected<0>()));
+
+constexpr unsigned int ui0000FEFF = 0x0000feffU;
+constexpr unsigned short usFEFF = 0xfeffU;
+static_assert(checkFoldResult<NotBadFoldResult>(
+    [](size_t n) constexpr {
+      return __builtin_memcmp(&ui0000FEFF, &usFEFF, n);
+    },
+    withN<1u>(), withExpected<LittleEndian ? 0 : -1>()));
+
+constexpr unsigned int ui08038700 = 0x08038700u;
+constexpr unsigned int ui08048600 = 0x08048600u;
+static_assert(checkFoldResult<NotBadFoldResult>(
+    [](size_t n) constexpr {
+      return __builtin_memcmp(&ui08038700, &ui08048600, n);
+    },
+    withN<sizeof(int)>(), withExpected<LittleEndian ? 1 : -1>()));
+}
+
 namespace WcscmpEtc {
   constexpr wchar_t kFoobar[6] = {L'f',L'o',L'o',L'b',L'a',L'r'};
   constexpr wchar_t kFoobazfoobar[12] = {L'f',L'o',L'o',L'b',L'a',L'z',L'f',L'o',L'o',L'b',L'a',L'r'};
@@ -187,6 +324,27 @@ namespace StrchrEtc {
   static_assert(__builtin_memchr(nullptr, 'x', 3) == nullptr); // expected-error {{not an integral constant}} expected-note {{dereferenced null}}
   static_assert(__builtin_memchr(nullptr, 'x', 0) == nullptr); // FIXME: Should we reject this?
 
+  extern struct Incomplete incomplete;
+  static_assert(__builtin_memchr(&incomplete, 0, 0u) == nullptr);
+  static_assert(__builtin_memchr(&incomplete, 0, 1u) == nullptr); // expected-error {{not an integral constant}} expected-note {{read of incomplete type 'struct Incomplete'}}
+
+  const unsigned char &u1 = 0xf0;
+  auto &&i1 = (const signed char []){-128}; // expected-warning {{compound literals are a C99-specific feature}}
+  static_assert(__builtin_memchr(&u1, -(0x0f + 1), 1) == &u1);
+  static_assert(__builtin_memchr(i1, 0x80, 1) == i1);
+
+  enum class E : unsigned char {};
+  struct EPair { E e, f; };
+  constexpr EPair ee{E{240}};
+  static_assert(__builtin_memchr(&ee.e, 240, 1) == &ee.e);
+
+  constexpr bool kBool[] = {false, true, false};
+  constexpr const bool *const kBoolPastTheEndPtr = kBool + 3;
+  static_assert(sizeof(bool) != 1u || __builtin_memchr(kBoolPastTheEndPtr - 3, 1, 99) == kBool + 1);
+  static_assert(sizeof(bool) != 1u || __builtin_memchr(kBool + 1, 0, 99) == kBoolPastTheEndPtr - 1);
+  static_assert(sizeof(bool) != 1u || __builtin_memchr(kBoolPastTheEndPtr - 3, -1, 3) == nullptr);
+  static_assert(sizeof(bool) != 1u || __builtin_memchr(kBoolPastTheEndPtr, 0, 1) == nullptr); // expected-error {{not an integral constant}} expected-note {{dereferenced one-past-the-end}}
+
   static_assert(__builtin_char_memchr(kStr, 'a', 0) == nullptr);
   static_assert(__builtin_char_memchr(kStr, 'a', 1) == kStr);
   static_assert(__builtin_char_memchr(kStr, '\0', 5) == nullptr);
@@ -212,6 +370,22 @@ namespace StrchrEtc {
   constexpr bool b = !memchr("hello", 'h', 3); // expected-error {{constant expression}} expected-note {{non-constexpr function 'memchr' cannot be used in a constant expression}}
 }
 
+namespace MultibyteElementTests::Memchr {
+constexpr unsigned int u04030201 = 0x04030201;
+static_assert(checkFoldResult<NotBadFoldResult>(
+    [](size_t n) constexpr {
+      return __builtin_memchr(&u04030201, *ByteOrderString - '0', n);
+    },
+    withN<1u>(), withExpected<&u04030201>()));
+
+constexpr unsigned int uED = 0xEDU;
+static_assert(checkFoldResult<NotBadFoldResult>(
+    [](size_t n) constexpr {
+      return __builtin_memchr(&uED, 0xED, n);
+    },
+    withN<1u>(), withExpected<LittleEndian ? &uED : nullptr>()));
+}
+
 namespace WcschrEtc {
   constexpr const wchar_t *kStr = L"abca\xffff\0dL";
   constexpr wchar_t kFoo[] = {L'f', L'o', L'o'};
@@ -269,15 +443,15 @@ namespace MemcpyEtc {
     wchar_t arr[4] = {1, 2, 3, 4};
     __builtin_wmemcpy(arr + a, arr + b, n);
     // expected-note at -1 2{{overlapping memory regions}}
-    // expected-note-re at -2 {{source is not a contiguous array of at least 2 elements of type '{{wchar_t|int}}'}}
-    // expected-note-re at -3 {{destination is not a contiguous array of at least 3 elements of type '{{wchar_t|int}}'}}
+    // expected-note at -2 {{source is not a contiguous array of at least 2 elements of type 'wchar_t'}}
+    // expected-note at -3 {{destination is not a contiguous array of at least 3 elements of type 'wchar_t'}}
     return result(arr);
   }
   constexpr int test_wmemmove(int a, int b, int n) {
     wchar_t arr[4] = {1, 2, 3, 4};
     __builtin_wmemmove(arr + a, arr + b, n);
-    // expected-note-re at -1 {{source is not a contiguous array of at least 2 elements of type '{{wchar_t|int}}'}}
-    // expected-note-re at -2 {{destination is not a contiguous array of at least 3 elements of type '{{wchar_t|int}}'}}
+    // expected-note at -1 {{source is not a contiguous array of at least 2 elements of type 'wchar_t'}}
+    // expected-note at -2 {{destination is not a contiguous array of at least 3 elements of type 'wchar_t'}}
     return result(arr);
   }
 




More information about the cfe-commits mailing list