Skip to content

Commit 75d5026

Browse files
Create StringEvalResult to hold result of compile-time string evaluation
Several users of compile-time string evaluation can meaningfully use the special case that compile-time string evaluation resolves to a string literal in source (for instance, to improve diagnostics). This changes Expr::tryEvaluateString to return a StringEvalResult, which can hold either a string literal and an offset or a std::string of evaluated characters.
1 parent be63894 commit 75d5026

File tree

7 files changed

+182
-91
lines changed

7 files changed

+182
-91
lines changed

clang/include/clang/AST/Expr.h

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -791,17 +791,25 @@ class Expr : public ValueStmt {
791791
const Expr *PtrExpression, ASTContext &Ctx,
792792
EvalResult &Status) const;
793793

794-
/// Fill \c Into with the first characters that can be constant-evaluated
795-
/// from this \c Expr . When encountering a null character, stop and return
796-
/// \c true (the null is not returned in \c Into ). Return \c false if
797-
/// evaluation runs off the end of the constant-evaluated string before it
798-
/// encounters a null character.
799-
bool tryEvaluateString(ASTContext &Ctx, std::string &Into) const;
800-
801-
/// If the current \c Expr can be evaluated to a pointer to a null-terminated
802-
/// constant string, return the constant string (without the terminating
803-
/// null).
804-
std::optional<std::string> tryEvaluateString(ASTContext &Ctx) const;
794+
class StringEvalResult {
795+
std::string Storage;
796+
const StringLiteral *SL;
797+
uint64_t Offset;
798+
799+
public:
800+
StringEvalResult(std::string Contents);
801+
StringEvalResult(const StringLiteral *SL, uint64_t Offset);
802+
803+
llvm::StringRef getString() const;
804+
bool getStringLiteral(const StringLiteral *&SL, uint64_t &Offset) const;
805+
};
806+
807+
/// If the current \c Expr can be evaluated to a pointer to a constant string,
808+
/// return the constant string. The string may not be NUL-terminated. If
809+
/// \c NullTerminated is supplied, it is set to whether there is at least one
810+
/// NUL character in the string.
811+
std::optional<StringEvalResult>
812+
tryEvaluateString(ASTContext &Ctx, bool *NullTerminated = nullptr) const;
805813

806814
/// Enumeration used to describe the kind of Null pointer constant
807815
/// returned from \c isNullPointerConstant().

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9268,7 +9268,7 @@ def err_expected_callable_argument : Error<
92689268
def note_building_builtin_dump_struct_call : Note<
92699269
"in call to printing function with arguments '(%0)' while dumping struct">;
92709270
def err_builtin_verbose_trap_arg : Error<
9271-
"argument to __builtin_verbose_trap must %select{be a pointer to a constant string|not contain $}0">;
9271+
"argument to __builtin_verbose_trap must %select{be a pointer to a constant NUL-terminated string|not contain $}0">;
92729272

92739273
def err_atomic_load_store_uses_lib : Error<
92749274
"atomic %select{load|store}0 requires runtime support that is not "

clang/lib/AST/ExprConstant.cpp

Lines changed: 107 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1920,9 +1920,17 @@ static bool EvaluateComplex(const Expr *E, ComplexValue &Res, EvalInfo &Info);
19201920
static bool EvaluateAtomic(const Expr *E, const LValue *This, APValue &Result,
19211921
EvalInfo &Info);
19221922
static 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);
19231932
static 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.
19281936
static 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

1804018113
template <typename T>

clang/lib/Analysis/UnsafeBufferUsage.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -726,12 +726,13 @@ static bool hasUnsafeFormatOrSArg(const CallExpr *Call, const Expr *&UnsafeArg,
726726
const Expr *Fmt = Call->getArg(FmtArgIdx);
727727

728728
if (auto *SL = dyn_cast<clang::StringLiteral>(Fmt->IgnoreParenImpCasts())) {
729+
std::optional<Expr::StringEvalResult> SER;
729730
StringRef FmtStr;
730731

731732
if (SL->getCharByteWidth() == 1)
732733
FmtStr = SL->getString();
733-
else if (auto EvaledFmtStr = SL->tryEvaluateString(Ctx))
734-
FmtStr = *EvaledFmtStr;
734+
else if ((SER = SL->tryEvaluateString(Ctx)))
735+
FmtStr = SER->getString();
735736
else
736737
goto CHECK_UNSAFE_PTR;
737738

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3477,8 +3477,9 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
34773477
llvm::DILocation *TrapLocation = Builder.getCurrentDebugLocation();
34783478
if (getDebugInfo()) {
34793479
TrapLocation = getDebugInfo()->CreateTrapFailureMessageFor(
3480-
TrapLocation, *E->getArg(0)->tryEvaluateString(getContext()),
3481-
*E->getArg(1)->tryEvaluateString(getContext()));
3480+
TrapLocation,
3481+
E->getArg(0)->tryEvaluateString(getContext())->getString(),
3482+
E->getArg(1)->tryEvaluateString(getContext())->getString());
34823483
}
34833484
ApplyDebugLocation ApplyTrapDI(*this, TrapLocation);
34843485
// Currently no attempt is made to prevent traps from being merged.

0 commit comments

Comments
 (0)