From e21432efa94079aa1a62f7d5ff3b47a92d3c8296 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Fri, 15 Nov 2013 02:10:04 +0000 Subject: [PATCH] Modern gcc is happy to constant evaluate __builtin_strlen in various cases where we didn't. Extend our constant evaluation for __builtin_strlen to handle any constant array of chars, not just string literals, to match. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@194762 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/ExprConstant.cpp | 59 +++++++++++++++------- test/SemaCXX/constant-expression-cxx11.cpp | 39 ++++++++++++++ 2 files changed, 81 insertions(+), 17 deletions(-) diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index cd984a251c..390cfe9cd2 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -6133,22 +6133,47 @@ bool IntExprEvaluator::VisitCallExpr(const CallExpr *E) { else Info.CCEDiag(E, diag::note_invalid_subexpr_in_const_expr); // Fall through. - case Builtin::BI__builtin_strlen: - // As an extension, we support strlen() and __builtin_strlen() as constant - // expressions when the argument is a string literal. - if (const StringLiteral *S - = dyn_cast(E->getArg(0)->IgnoreParenImpCasts())) { + case Builtin::BI__builtin_strlen: { + // As an extension, we support __builtin_strlen() as a constant expression, + // and support folding strlen() to a constant. + LValue String; + if (!EvaluatePointer(E->getArg(0), String, Info)) + return false; + + // Fast path: if it's a string literal, search the string value. + if (const StringLiteral *S = dyn_cast_or_null( + String.getLValueBase().dyn_cast())) { // The string literal may have embedded null characters. Find the first // one and truncate there. - StringRef Str = S->getString(); - StringRef::size_type Pos = Str.find(0); - if (Pos != StringRef::npos) - Str = Str.substr(0, Pos); - - return Success(Str.size(), E); + StringRef Str = S->getBytes(); + int64_t Off = String.Offset.getQuantity(); + if (Off >= 0 && (uint64_t)Off <= (uint64_t)Str.size() && + S->getCharByteWidth() == 1) { + Str = Str.substr(Off); + + StringRef::size_type Pos = Str.find(0); + if (Pos != StringRef::npos) + Str = Str.substr(0, Pos); + + return Success(Str.size(), E); + } + + // Fall through to slow path to issue appropriate diagnostic. } - - return Error(E); + + // Slow path: scan the bytes of the string looking for the terminating 0. + QualType CharTy = E->getArg(0)->getType()->getPointeeType(); + for (uint64_t Strlen = 0; /**/; ++Strlen) { + APValue Char; + if (!handleLValueToRValueConversion(Info, E, CharTy, String, Char) || + !Char.isInt()) + return false; + if (!Char.getInt()) + return Success(Strlen, E); + if (!HandleLValueArrayAdjustment(Info, E, String, CharTy, 1)) + return false; + } + } case Builtin::BI__atomic_always_lock_free: case Builtin::BI__atomic_is_lock_free: @@ -6426,8 +6451,8 @@ bool DataRecursiveIntBinOpEvaluator:: // Handle cases like (unsigned long)&a + 4. if (E->isAdditiveOp() && LHSVal.isLValue() && RHSVal.isInt()) { Result = LHSVal; - CharUnits AdditionalOffset = CharUnits::fromQuantity( - RHSVal.getInt().getZExtValue()); + CharUnits AdditionalOffset = + CharUnits::fromQuantity(RHSVal.getInt().getZExtValue()); if (E->getOpcode() == BO_Add) Result.getLValueOffset() += AdditionalOffset; else @@ -6439,8 +6464,8 @@ bool DataRecursiveIntBinOpEvaluator:: if (E->getOpcode() == BO_Add && RHSVal.isLValue() && LHSVal.isInt()) { Result = RHSVal; - Result.getLValueOffset() += CharUnits::fromQuantity( - LHSVal.getInt().getZExtValue()); + Result.getLValueOffset() += + CharUnits::fromQuantity(LHSVal.getInt().getZExtValue()); return true; } diff --git a/test/SemaCXX/constant-expression-cxx11.cpp b/test/SemaCXX/constant-expression-cxx11.cpp index 664daf081b..6724be7c82 100644 --- a/test/SemaCXX/constant-expression-cxx11.cpp +++ b/test/SemaCXX/constant-expression-cxx11.cpp @@ -1824,3 +1824,42 @@ namespace PR17800 { } constexpr int k = run<1, 2, 3>(); } + +namespace BuiltinStrlen { + constexpr const char *a = "foo\0quux"; + constexpr char b[] = "foo\0quux"; + constexpr int f() { return 'u'; } + constexpr char c[] = { 'f', 'o', 'o', 0, 'q', f(), 'u', 'x', 0 }; + + static_assert(__builtin_strlen("foo") == 3, ""); + static_assert(__builtin_strlen("foo\0quux") == 3, ""); + static_assert(__builtin_strlen("foo\0quux" + 4) == 4, ""); + + constexpr bool check(const char *p) { + return __builtin_strlen(p) == 3 && + __builtin_strlen(p + 1) == 2 && + __builtin_strlen(p + 2) == 1 && + __builtin_strlen(p + 3) == 0 && + __builtin_strlen(p + 4) == 4 && + __builtin_strlen(p + 5) == 3 && + __builtin_strlen(p + 6) == 2 && + __builtin_strlen(p + 7) == 1 && + __builtin_strlen(p + 8) == 0; + } + + static_assert(check(a), ""); + static_assert(check(b), ""); + static_assert(check(c), ""); + + constexpr int over1 = __builtin_strlen(a + 9); // expected-error {{constant expression}} expected-note {{one-past-the-end}} + constexpr int over2 = __builtin_strlen(b + 9); // expected-error {{constant expression}} expected-note {{one-past-the-end}} + constexpr int over3 = __builtin_strlen(c + 9); // expected-error {{constant expression}} expected-note {{one-past-the-end}} + + constexpr int under1 = __builtin_strlen(a - 1); // expected-error {{constant expression}} expected-note {{cannot refer to element -1}} + constexpr int under2 = __builtin_strlen(b - 1); // expected-error {{constant expression}} expected-note {{cannot refer to element -1}} + constexpr int under3 = __builtin_strlen(c - 1); // expected-error {{constant expression}} expected-note {{cannot refer to element -1}} + + // FIXME: The diagnostic here could be better. + constexpr char d[] = { 'f', 'o', 'o' }; // no nul terminator. + constexpr int bad = __builtin_strlen(d); // expected-error {{constant expression}} expected-note {{one-past-the-end}} +} -- 2.40.0