if (IsStringLiteralCall(E))
return Success(E);
- switch (E->getBuiltinCallee()) {
+ switch (unsigned BuiltinOp = E->getBuiltinCallee()) {
case Builtin::BI__builtin_addressof:
return EvaluateLValue(E->getArg(0), Result, Info);
case Builtin::BI__builtin_assume_aligned: {
return true;
}
+
+ case Builtin::BIstrchr:
+ case Builtin::BImemchr:
+ if (Info.getLangOpts().CPlusPlus11)
+ Info.CCEDiag(E, diag::note_constexpr_invalid_function)
+ << /*isConstexpr*/0 << /*isConstructor*/0
+ << (BuiltinOp == Builtin::BIstrchr ? "'strchr'" : "'memchr'");
+ else
+ Info.CCEDiag(E, diag::note_invalid_subexpr_in_const_expr);
+ // Fall through.
+ case Builtin::BI__builtin_strchr:
+ case Builtin::BI__builtin_memchr: {
+ if (!Visit(E->getArg(0)))
+ return false;
+ APSInt Desired;
+ if (!EvaluateInteger(E->getArg(1), Desired, Info))
+ return false;
+ uint64_t MaxLength = uint64_t(-1);
+ if (BuiltinOp != Builtin::BIstrchr &&
+ BuiltinOp != Builtin::BI__builtin_strchr) {
+ APSInt N;
+ if (!EvaluateInteger(E->getArg(2), N, Info))
+ return false;
+ MaxLength = N.getExtValue();
+ }
+
+ QualType CharTy = Info.Ctx.CharTy;
+ bool IsStrchr = (BuiltinOp != Builtin::BImemchr &&
+ BuiltinOp != Builtin::BI__builtin_memchr);
+
+ // strchr compares directly to the passed integer, and therefore
+ // always fails if given an int that is not a char.
+ if (IsStrchr &&
+ !APSInt::isSameValue(HandleIntToIntCast(Info, E, CharTy,
+ E->getArg(1)->getType(),
+ Desired),
+ Desired))
+ return ZeroInitialization(E);
+
+ // memchr compares by converting both sides to unsigned char. That's also
+ // correct for strchr if we get this far.
+ uint64_t DesiredVal = Desired.trunc(Info.Ctx.getCharWidth()).getZExtValue();
+
+ for (; MaxLength; --MaxLength) {
+ APValue Char;
+ if (!handleLValueToRValueConversion(Info, E, CharTy, Result, Char) ||
+ !Char.isInt())
+ return false;
+ if (Char.getInt().getZExtValue() == DesiredVal)
+ return true;
+ if (IsStrchr && !Char.getInt())
+ break;
+ if (!HandleLValueArrayAdjustment(Info, E, Result, CharTy, 1))
+ return false;
+ }
+ // Not found: return nullptr.
+ return ZeroInitialization(E);
+ }
+
default:
return ExprEvaluatorBaseTy::VisitCallExpr(E);
}
}
// Slow path: scan the bytes of the string looking for the terminating 0.
- QualType CharTy = E->getArg(0)->getType()->getPointeeType();
+ QualType CharTy = Info.Ctx.CharTy;
for (uint64_t Strlen = 0; /**/; ++Strlen) {
APValue Char;
if (!handleLValueToRValueConversion(Info, E, CharTy, String, Char) ||
}
bool StopAtNull = (BuiltinOp != Builtin::BImemcmp &&
BuiltinOp != Builtin::BI__builtin_memcmp);
- QualType CharTy = E->getArg(0)->getType()->getPointeeType();
+ QualType CharTy = Info.Ctx.CharTy;
for (; MaxLength; --MaxLength) {
APValue Char1, Char2;
if (!handleLValueToRValueConversion(Info, E, CharTy, String1, Char1) ||
// RUN: %clang_cc1 %s -std=c++1z -fsyntax-only -verify -pedantic
+// RUN: %clang_cc1 %s -std=c++1z -fsyntax-only -verify -pedantic -fno-signed-char
# 4 "/usr/include/string.h" 1 3 4
extern "C" {
extern int strcmp(const char *s1, const char *s2);
extern int strncmp(const char *s1, const char *s2, size_t n);
extern int memcmp(const char *s1, const char *s2, size_t n); // expected-note {{here}}
+
+ extern char *strchr(const char *s, int c);
+ extern void *memchr(const void *s, int c, size_t n);
}
-# 15 "SemaCXX/constexpr-string.cpp" 2
+# 19 "SemaCXX/constexpr-string.cpp" 2
namespace Strlen {
constexpr int n = __builtin_strlen("hello"); // ok
constexpr int m = strlen("hello"); // expected-error {{constant expression}} expected-note {{non-constexpr function 'strlen' 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 StrchrEtc {
+ constexpr const char *kStr = "abca\xff\0d";
+ constexpr char kFoo[] = {'f', 'o', 'o'};
+ static_assert(__builtin_strchr(kStr, 'a') == kStr);
+ static_assert(__builtin_strchr(kStr, 'b') == kStr + 1);
+ static_assert(__builtin_strchr(kStr, 'c') == kStr + 2);
+ static_assert(__builtin_strchr(kStr, 'd') == nullptr);
+ static_assert(__builtin_strchr(kStr, 'e') == nullptr);
+ static_assert(__builtin_strchr(kStr, '\0') == kStr + 5);
+ static_assert(__builtin_strchr(kStr, 'a' + 256) == nullptr);
+ static_assert(__builtin_strchr(kStr, 'a' - 256) == nullptr);
+ static_assert(__builtin_strchr(kStr, '\xff') == kStr + 4);
+ static_assert(__builtin_strchr(kStr, '\xff' + 256) == nullptr);
+ static_assert(__builtin_strchr(kStr, '\xff' - 256) == nullptr);
+ static_assert(__builtin_strchr(kFoo, 'o') == kFoo + 1);
+ static_assert(__builtin_strchr(kFoo, 'x') == nullptr); // expected-error {{not an integral constant}} expected-note {{dereferenced one-past-the-end}}
+ static_assert(__builtin_strchr(nullptr, 'x') == nullptr); // expected-error {{not an integral constant}} expected-note {{dereferenced null}}
+
+ static_assert(__builtin_memchr(kStr, 'a', 0) == nullptr);
+ static_assert(__builtin_memchr(kStr, 'a', 1) == kStr);
+ static_assert(__builtin_memchr(kStr, '\0', 5) == nullptr);
+ static_assert(__builtin_memchr(kStr, '\0', 6) == kStr + 5);
+ static_assert(__builtin_memchr(kStr, '\xff', 8) == kStr + 4);
+ static_assert(__builtin_memchr(kStr, '\xff' + 256, 8) == kStr + 4);
+ static_assert(__builtin_memchr(kStr, '\xff' - 256, 8) == kStr + 4);
+ static_assert(__builtin_memchr(kFoo, 'x', 3) == nullptr);
+ static_assert(__builtin_memchr(kFoo, 'x', 4) == nullptr); // expected-error {{not an integral constant}} expected-note {{dereferenced one-past-the-end}}
+ 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?
+
+ constexpr bool a = !strchr("hello", 'h'); // expected-error {{constant expression}} expected-note {{non-constexpr function 'strchr' cannot be used in a constant expression}}
+ constexpr bool b = !memchr("hello", 'h', 3); // expected-error {{constant expression}} expected-note {{non-constexpr function 'memchr' cannot be used in a constant expression}}
+}