From: Richard Smith Date: Fri, 11 Nov 2016 23:43:35 +0000 (+0000) Subject: [c++1z] Add constant-folding support for strcmp, strncmp, and memcmp, to X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f74c116bcc537e53097d3954bac3746cf5db2be5;p=clang [c++1z] Add constant-folding support for strcmp, strncmp, and memcmp, to support constexpr char_traits. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@286678 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 338f006ce0..8096e0e72e 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -7078,6 +7078,56 @@ bool IntExprEvaluator::VisitCallExpr(const CallExpr *E) { } } + case Builtin::BIstrcmp: + case Builtin::BIstrncmp: + case Builtin::BImemcmp: + // A call to strlen is not a constant expression. + if (Info.getLangOpts().CPlusPlus11) + Info.CCEDiag(E, diag::note_constexpr_invalid_function) + << /*isConstexpr*/0 << /*isConstructor*/0 + << (BuiltinOp == Builtin::BIstrncmp ? "'strncmp'" : + BuiltinOp == Builtin::BImemcmp ? "'memcmp'" : + "'strcmp'"); + else + Info.CCEDiag(E, diag::note_invalid_subexpr_in_const_expr); + // Fall through. + case Builtin::BI__builtin_strcmp: + case Builtin::BI__builtin_strncmp: + case Builtin::BI__builtin_memcmp: { + LValue String1, String2; + if (!EvaluatePointer(E->getArg(0), String1, Info) || + !EvaluatePointer(E->getArg(1), String2, Info)) + return false; + uint64_t MaxLength = uint64_t(-1); + if (BuiltinOp != Builtin::BIstrcmp && + BuiltinOp != Builtin::BI__builtin_strcmp) { + APSInt N; + if (!EvaluateInteger(E->getArg(2), N, Info)) + return false; + MaxLength = N.getExtValue(); + } + bool StopAtNull = (BuiltinOp != Builtin::BImemcmp && + BuiltinOp != Builtin::BI__builtin_memcmp); + QualType CharTy = E->getArg(0)->getType()->getPointeeType(); + for (; MaxLength; --MaxLength) { + APValue Char1, Char2; + if (!handleLValueToRValueConversion(Info, E, CharTy, String1, Char1) || + !handleLValueToRValueConversion(Info, E, CharTy, String2, Char2) || + !Char1.isInt() || !Char2.isInt()) + return false; + if (Char1.getInt() != Char2.getInt()) + return Success(Char1.getInt() < Char2.getInt() ? -1 : 1, E); + 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)) + return false; + } + // We hit the strncmp / memcmp limit. + return Success(0, E); + } + case Builtin::BI__atomic_always_lock_free: case Builtin::BI__atomic_is_lock_free: case Builtin::BI__c11_atomic_is_lock_free: { diff --git a/test/SemaCXX/constexpr-string.cpp b/test/SemaCXX/constexpr-string.cpp new file mode 100644 index 0000000000..e614d43b30 --- /dev/null +++ b/test/SemaCXX/constexpr-string.cpp @@ -0,0 +1,68 @@ +// RUN: %clang_cc1 %s -std=c++1z -fsyntax-only -verify -pedantic + +# 4 "/usr/include/string.h" 1 3 4 +extern "C" { + typedef decltype(sizeof(int)) size_t; + + extern size_t strlen(const char *p); + + 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}} +} + +# 15 "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}} + + // Make sure we can evaluate a call to strlen. + int arr[3]; // expected-note {{here}} + int k = arr[strlen("hello")]; // expected-warning {{array index 5}} +} + +namespace StrcmpEtc { + constexpr char kFoobar[6] = {'f','o','o','b','a','r'}; + constexpr char kFoobazfoobar[12] = {'f','o','o','b','a','z','f','o','o','b','a','r'}; + + static_assert(__builtin_strcmp("abab", "abab") == 0); + static_assert(__builtin_strcmp("abab", "abba") == -1); + static_assert(__builtin_strcmp("abab", "abaa") == 1); + static_assert(__builtin_strcmp("ababa", "abab") == 1); + static_assert(__builtin_strcmp("abab", "ababa") == -1); + static_assert(__builtin_strcmp("abab\0banana", "abab") == 0); + static_assert(__builtin_strcmp("abab", "abab\0banana") == 0); + static_assert(__builtin_strcmp("abab\0banana", "abab\0canada") == 0); + static_assert(__builtin_strcmp(0, "abab") == 0); // expected-error {{not an integral constant}} expected-note {{dereferenced null}} + static_assert(__builtin_strcmp("abab", 0) == 0); // expected-error {{not an integral constant}} expected-note {{dereferenced null}} + + static_assert(__builtin_strcmp(kFoobar, kFoobazfoobar) == -1); // FIXME: Should we reject this? + static_assert(__builtin_strcmp(kFoobar, kFoobazfoobar + 6) == 0); // expected-error {{not an integral constant}} expected-note {{dereferenced one-past-the-end}} + + static_assert(__builtin_strncmp("abaa", "abba", 5) == -1); + static_assert(__builtin_strncmp("abaa", "abba", 4) == -1); + static_assert(__builtin_strncmp("abaa", "abba", 3) == -1); + static_assert(__builtin_strncmp("abaa", "abba", 2) == 0); + static_assert(__builtin_strncmp("abaa", "abba", 1) == 0); + static_assert(__builtin_strncmp("abaa", "abba", 0) == 0); + static_assert(__builtin_strncmp(0, 0, 0) == 0); + static_assert(__builtin_strncmp("abab\0banana", "abab\0canada", 100) == 0); + + static_assert(__builtin_strncmp(kFoobar, kFoobazfoobar, 6) == -1); + static_assert(__builtin_strncmp(kFoobar, kFoobazfoobar, 7) == -1); // FIXME: Should we reject this? + static_assert(__builtin_strncmp(kFoobar, kFoobazfoobar + 6, 6) == 0); + static_assert(__builtin_strncmp(kFoobar, kFoobazfoobar + 6, 7) == 0); // expected-error {{not an integral constant}} expected-note {{dereferenced one-past-the-end}} + + static_assert(__builtin_memcmp("abaa", "abba", 3) == -1); + static_assert(__builtin_memcmp("abaa", "abba", 2) == 0); + static_assert(__builtin_memcmp(0, 0, 0) == 0); + static_assert(__builtin_memcmp("abab\0banana", "abab\0banana", 100) == 0); // expected-error {{not an integral constant}} expected-note {{dereferenced one-past-the-end}} + static_assert(__builtin_memcmp("abab\0banana", "abab\0canada", 100) == -1); // FIXME: Should we reject this? + static_assert(__builtin_memcmp("abab\0banana", "abab\0canada", 7) == -1); + static_assert(__builtin_memcmp("abab\0banana", "abab\0canada", 6) == -1); + static_assert(__builtin_memcmp("abab\0banana", "abab\0canada", 5) == 0); + + 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}} +} diff --git a/test/SemaCXX/constexpr-strlen.cpp b/test/SemaCXX/constexpr-strlen.cpp deleted file mode 100644 index 5e28e7f0ce..0000000000 --- a/test/SemaCXX/constexpr-strlen.cpp +++ /dev/null @@ -1,15 +0,0 @@ -// RUN: %clang_cc1 %s -std=c++11 -fsyntax-only -verify -pedantic - -# 1 "/usr/include/string.h" 1 3 4 -extern "C" { - typedef decltype(sizeof(int)) size_t; - extern size_t strlen(const char *p); -} - -# 10 "SemaCXX/constexpr-strlen.cpp" 2 -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}} - -// Make sure we can evaluate a call to strlen. -int arr[3]; // expected-note {{here}} -int k = arr[strlen("hello")]; // expected-warning {{array index 5}}