@@ -1920,9 +1920,17 @@ static bool EvaluateComplex(const Expr *E, ComplexValue &Res, EvalInfo &Info);
19201920static bool EvaluateAtomic(const Expr *E, const LValue *This, APValue &Result,
19211921 EvalInfo &Info);
19221922static bool EvaluateAsRValue(EvalInfo &Info, const Expr *E, APValue &Result);
1923+ static bool EvaluateStringAsLValue(EvalInfo &Info, const Expr *E,
1924+ QualType &CharTy, LValue &String);
1925+ static const StringLiteral *StringLValueIsLiteral(EvalInfo &Info,
1926+ LValue &String,
1927+ QualType CharTy,
1928+ uint64_t &Offset);
1929+ template <typename CharAction>
1930+ static bool IterateStringLValue(EvalInfo &Info, const Expr *E, QualType CharTy,
1931+ LValue &String, CharAction &&Action);
19231932static bool EvaluateBuiltinStrLen(const Expr *E, uint64_t &Result,
1924- EvalInfo &Info,
1925- std::string *StringResult = nullptr);
1933+ EvalInfo &Info);
19261934
19271935/// Evaluate an integer or fixed point expression into an APResult.
19281936static bool EvaluateFixedPointOrInteger(const Expr *E, APFixedPoint &Result,
@@ -17950,14 +17958,12 @@ bool Expr::tryEvaluateObjectSize(uint64_t &Result, ASTContext &Ctx,
1795017958 return tryEvaluateBuiltinObjectSize(this, Type, Info, Result);
1795117959}
1795217960
17953- static bool EvaluateBuiltinStrLen( const Expr *E, uint64_t &Result ,
17954- EvalInfo &Info, std::string *StringResult ) {
17961+ static bool EvaluateStringAsLValue(EvalInfo &Info, const Expr *E,
17962+ QualType &CharTy, LValue &String ) {
1795517963 QualType Ty = E->getType();
1795617964 if (!E->isPRValue())
1795717965 return false;
1795817966
17959- LValue String;
17960- QualType CharTy;
1796117967 if (Ty->canDecayToPointerType()) {
1796217968 if (E->isGLValue()) {
1796317969 if (!EvaluateLValue(E, String, Info))
@@ -17982,8 +17988,13 @@ static bool EvaluateBuiltinStrLen(const Expr *E, uint64_t &Result,
1798217988 } else {
1798317989 return false;
1798417990 }
17991+ return true;
17992+ }
1798517993
17986- // Fast path: if it's a string literal, search the string value.
17994+ static const StringLiteral *StringLValueIsLiteral(EvalInfo &Info,
17995+ LValue &String,
17996+ QualType CharTy,
17997+ uint64_t &Offset) {
1798717998 if (const StringLiteral *S = dyn_cast_or_null<StringLiteral>(
1798817999 String.getLValueBase().dyn_cast<const Expr *>())) {
1798918000 StringRef Str = S->getBytes();
@@ -17992,49 +18003,111 @@ static bool EvaluateBuiltinStrLen(const Expr *E, uint64_t &Result,
1799218003 S->getCharByteWidth() == 1 &&
1799318004 // FIXME: Add fast-path for wchar_t too.
1799418005 Info.Ctx.hasSameUnqualifiedType(CharTy, Info.Ctx.CharTy)) {
17995- Str = Str.substr(Off);
17996-
17997- StringRef::size_type Pos = Str.find(0);
17998- if (Pos != StringRef::npos)
17999- Str = Str.substr(0, Pos);
18000-
18001- Result = Str.size();
18002- if (StringResult)
18003- *StringResult = Str;
18004- return true;
18006+ Offset = static_cast<uint64_t>(Off);
18007+ return S;
1800518008 }
18006-
18007- // Fall through to slow path.
1800818009 }
18010+ return nullptr;
18011+ }
1800918012
18010- // Slow path: scan the bytes of the string looking for the terminating 0.
18011- for (uint64_t Strlen = 0; /**/; ++Strlen) {
18013+ template <typename CharAction>
18014+ static bool IterateStringLValue(EvalInfo &Info, const Expr *E, QualType CharTy,
18015+ LValue &String, CharAction &&Action) {
18016+ while (true) {
1801218017 APValue Char;
1801318018 if (!handleLValueToRValueConversion(Info, E, CharTy, String, Char) ||
1801418019 !Char.isInt())
1801518020 return false;
18016- if (!Char.getInt()) {
18017- Result = Strlen;
18021+ if (!Action(Char.getInt().getExtValue()))
1801818022 return true;
18019- } else if (StringResult)
18020- StringResult->push_back(Char.getInt().getExtValue());
1802118023 if (!HandleLValueArrayAdjustment(Info, E, String, CharTy, 1))
1802218024 return false;
1802318025 }
1802418026}
1802518027
18026- bool Expr::tryEvaluateString(ASTContext &Ctx, std::string &StringResult) const {
18028+ static bool EvaluateBuiltinStrLen(const Expr *E, uint64_t &Result,
18029+ EvalInfo &Info) {
18030+ LValue String;
18031+ QualType CharTy;
18032+ if (!EvaluateStringAsLValue(Info, E, CharTy, String))
18033+ return false;
18034+
18035+ // Fast path: if it's a string literal, search the string value.
18036+ uint64_t Off;
18037+ if (const auto *S = StringLValueIsLiteral(Info, String, CharTy, Off)) {
18038+ StringRef Str = S->getBytes().substr(Off);
18039+
18040+ StringRef::size_type Pos = Str.find(0);
18041+ if (Pos != StringRef::npos)
18042+ Str = Str.substr(0, Pos);
18043+
18044+ Result = Str.size();
18045+ return true;
18046+ }
18047+
18048+ // Slow path: scan the bytes of the string looking for the terminating 0.
18049+ Result = 0;
18050+ return IterateStringLValue(Info, E, CharTy, String, [&](int Char) {
18051+ if (Char) {
18052+ Result++;
18053+ return true;
18054+ } else
18055+ return false;
18056+ });
18057+ }
18058+
18059+ Expr::StringEvalResult::StringEvalResult(const StringLiteral *SL,
18060+ uint64_t Offset)
18061+ : SL(SL), Offset(Offset) {}
18062+
18063+ Expr::StringEvalResult::StringEvalResult(std::string Contents)
18064+ : Storage(std::move(Contents)), SL(nullptr), Offset(0) {}
18065+
18066+ llvm::StringRef Expr::StringEvalResult::getString() const {
18067+ return SL ? SL->getBytes().substr(Offset) : Storage;
18068+ }
18069+
18070+ bool Expr::StringEvalResult::getStringLiteral(const StringLiteral *&SL,
18071+ uint64_t &Offset) const {
18072+ if (this->SL) {
18073+ SL = this->SL;
18074+ Offset = this->Offset;
18075+ return true;
18076+ }
18077+ return false;
18078+ }
18079+
18080+ std::optional<Expr::StringEvalResult>
18081+ Expr::tryEvaluateString(ASTContext &Ctx, bool *NullTerminated) const {
18082+ if (NullTerminated)
18083+ *NullTerminated = false;
18084+
1802718085 Expr::EvalStatus Status;
1802818086 EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantFold);
18029- uint64_t Result;
18030- return EvaluateBuiltinStrLen(this, Result, Info, &StringResult);
18031- }
18087+ LValue String;
18088+ QualType CharTy;
18089+ if (!EvaluateStringAsLValue(Info, this, CharTy, String))
18090+ return {};
18091+
18092+ uint64_t Off;
18093+ if (const auto *S = StringLValueIsLiteral(Info, String, CharTy, Off)) {
18094+ if (NullTerminated)
18095+ *NullTerminated = true;
18096+ return StringEvalResult(S, Off);
18097+ }
18098+
18099+ std::string Result;
18100+ bool NTFound = IterateStringLValue(Info, this, CharTy, String, [&](int Char) {
18101+ if (Char) {
18102+ Result.push_back(Char);
18103+ return true;
18104+ } else
18105+ return false;
18106+ });
1803218107
18033- std::optional<std::string> Expr::tryEvaluateString(ASTContext &Ctx) const {
18034- std::string StringResult;
18035- if (tryEvaluateString(Ctx, StringResult))
18036- return StringResult;
18037- return {};
18108+ if (NullTerminated)
18109+ *NullTerminated = NTFound;
18110+ return StringEvalResult(Result);
1803818111}
1803918112
1804018113template <typename T>
0 commit comments