Skip to content

Commit c351795

Browse files
Fix (part of) the constexpr problem
1 parent 03309c7 commit c351795

File tree

4 files changed

+150
-106
lines changed

4 files changed

+150
-106
lines changed

clang/include/clang/AST/Expr.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -809,7 +809,8 @@ class Expr : public ValueStmt {
809809
/// \c NullTerminated is supplied, it is set to whether there is at least one
810810
/// NUL character in the string.
811811
std::optional<StringEvalResult>
812-
tryEvaluateString(ASTContext &Ctx, bool *NullTerminated = nullptr) const;
812+
tryEvaluateString(ASTContext &Ctx, bool *NullTerminated = nullptr,
813+
bool InConstantContext = false) const;
813814

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

clang/lib/AST/ExprConstant.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18074,12 +18074,18 @@ bool Expr::StringEvalResult::getStringLiteral(const StringLiteral *&SL,
1807418074
}
1807518075

1807618076
std::optional<Expr::StringEvalResult>
18077-
Expr::tryEvaluateString(ASTContext &Ctx, bool *NullTerminated) const {
18077+
Expr::tryEvaluateString(ASTContext &Ctx, bool *NullTerminated,
18078+
bool InConstantContext) const {
1807818079
if (NullTerminated)
1807918080
*NullTerminated = false;
1808018081

1808118082
Expr::EvalStatus Status;
18082-
EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantFold);
18083+
EvalInfo Info(Ctx, Status,
18084+
(InConstantContext &&
18085+
(Ctx.getLangOpts().CPlusPlus || Ctx.getLangOpts().C23))
18086+
? EvalInfo::EM_ConstantExpression
18087+
: EvalInfo::EM_ConstantFold);
18088+
Info.InConstantContext = InConstantContext;
1808318089
LValue String;
1808418090
QualType CharTy;
1808518091
if (!EvaluateStringAsLValue(Info, this, CharTy, String))

clang/lib/Sema/SemaChecking.cpp

Lines changed: 124 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -5938,37 +5938,80 @@ static void CheckFormatString(
59385938
llvm::SmallBitVector &CheckedVarArgs, UncoveredArgHandler &UncoveredArg,
59395939
bool IgnoreStringsWithoutSpecifiers);
59405940

5941-
enum StringLiteralConstantEvaluationResult {
5942-
SLCER_NotEvaluated,
5943-
SLCER_NotNullTerminated,
5944-
SLCER_Evaluated,
5941+
class FormatStringFinder {
5942+
enum StringLiteralConstantEvaluationResult {
5943+
SLCER_NotEvaluated,
5944+
SLCER_NotNullTerminated,
5945+
SLCER_Evaluated,
5946+
};
5947+
5948+
Sema &S;
5949+
const StringLiteral *ReferenceFormatString;
5950+
ArrayRef<const Expr *> Args;
5951+
llvm::SmallBitVector &CheckedVarArgs;
5952+
UncoveredArgHandler &UncoveredArg;
5953+
Sema::FormatArgumentPassingKind APK;
5954+
Sema::FormatStringType Type;
5955+
Sema::VariadicCallType CallType;
5956+
unsigned format_idx;
5957+
unsigned firstDataArg;
5958+
bool InFunctionCall;
5959+
bool IgnoreStringsWithoutSpecifiers;
5960+
5961+
FormatStringFinder(Sema &S, const StringLiteral *ReferenceFormatString,
5962+
ArrayRef<const Expr *> Args,
5963+
llvm::SmallBitVector &CheckedVarArgs,
5964+
UncoveredArgHandler &UncoveredArg,
5965+
Sema::FormatArgumentPassingKind APK,
5966+
Sema::FormatStringType Type,
5967+
Sema::VariadicCallType CallType, unsigned format_idx,
5968+
unsigned firstDataArg, bool InFunctionCall)
5969+
: S(S), ReferenceFormatString(ReferenceFormatString), Args(Args),
5970+
CheckedVarArgs(CheckedVarArgs), UncoveredArg(UncoveredArg), APK(APK),
5971+
Type(Type), CallType(CallType), format_idx(format_idx),
5972+
firstDataArg(firstDataArg), InFunctionCall(InFunctionCall),
5973+
IgnoreStringsWithoutSpecifiers(false) {}
5974+
5975+
/// Attempt to fold \c E into a constant string that \c checkFormatStringExpr
5976+
/// can use. If \c E folds to a string literal, that string literal will be
5977+
/// used for diagnostics. If \c E has a constant string value but it does not
5978+
/// fold to a literal (for instance, ("%"s + "i"s).c_str() constant-folds to
5979+
/// "%i"), a <scratch space> pseudo-source file will be allocated, containing
5980+
/// a string literal representation of the constant string, and format
5981+
/// diagnostics will point to it.
5982+
static StringLiteralConstantEvaluationResult
5983+
EvaluateStringAndCreateLiteral(Sema &S, const Expr *E,
5984+
bool IsConstantEvaluation,
5985+
const StringLiteral *&SL, uint64_t &Offset);
5986+
5987+
StringLiteralCheckType
5988+
checkEvaluateLiteral(const Expr *E, llvm::APSInt Offset,
5989+
bool IsConstantInitialization = false);
5990+
StringLiteralCheckType check(const Expr *E, llvm::APSInt Offset);
5991+
5992+
public:
5993+
// Determine if an expression is a string literal or constant string.
5994+
// If this function returns false on the arguments to a function expecting a
5995+
// format string, we will usually need to emit a warning.
5996+
// True string literals are then checked by CheckFormatString.
5997+
static StringLiteralCheckType
5998+
check(Sema &S, const StringLiteral *ReferenceFormatString,
5999+
ArrayRef<const Expr *> Args, unsigned FormatIdx, unsigned FirstDataArg,
6000+
Sema::FormatArgumentPassingKind APK, Sema::FormatStringType FST,
6001+
Sema::VariadicCallType CallType, llvm::SmallBitVector &CheckedVarArgs,
6002+
UncoveredArgHandler &UncoveredArg) {
6003+
FormatStringFinder Finder(S, ReferenceFormatString, Args, CheckedVarArgs,
6004+
UncoveredArg, APK, FST, CallType, FormatIdx,
6005+
FirstDataArg, true);
6006+
const Expr *E = Args[FormatIdx];
6007+
return S.isConstantEvaluatedContext()
6008+
? Finder.checkEvaluateLiteral(E, llvm::APSInt(64, false) = 0)
6009+
: Finder.check(E, llvm::APSInt(64, false) = 0);
6010+
}
59456011
};
59466012

5947-
/// Attempt to fold \c E into a constant string that \c checkFormatStringExpr
5948-
/// can use. If \c E folds to a string literal, that string literal will be used
5949-
/// for diagnostics. If \c E has a constant string value but it does not fold to
5950-
/// a literal (for instance, ("%"s + "i"s).c_str() constant-folds to "%i"), a
5951-
/// <scratch space> pseudo-source file will be allocated, containing a string
5952-
/// literal representation of the constant string, and format diagnostics will
5953-
/// point to it.
5954-
static StringLiteralConstantEvaluationResult
5955-
EvaluateStringAndCreateLiteral(Sema &S, const Expr *E, const StringLiteral *&SL,
5956-
uint64_t &Offset);
5957-
5958-
// Determine if an expression is a string literal or constant string.
5959-
// If this function returns false on the arguments to a function expecting a
5960-
// format string, we will usually need to emit a warning.
5961-
// True string literals are then checked by CheckFormatString.
5962-
static StringLiteralCheckType checkFormatStringExpr(
5963-
Sema &S, const StringLiteral *ReferenceFormatString, const Expr *E,
5964-
ArrayRef<const Expr *> Args, Sema::FormatArgumentPassingKind APK,
5965-
unsigned format_idx, unsigned firstDataArg, Sema::FormatStringType Type,
5966-
Sema::VariadicCallType CallType, bool InFunctionCall,
5967-
llvm::SmallBitVector &CheckedVarArgs, UncoveredArgHandler &UncoveredArg,
5968-
llvm::APSInt Offset, bool IgnoreStringsWithoutSpecifiers = false) {
5969-
if (S.isConstantEvaluatedContext())
5970-
return SLCT_NotALiteral;
5971-
tryAgain:
6013+
StringLiteralCheckType FormatStringFinder::check(const Expr *E,
6014+
llvm::APSInt Offset) {
59726015
assert(Offset.isSigned() && "invalid offset");
59736016

59746017
if (E->isTypeDependent() || E->isValueDependent())
@@ -5984,9 +6027,14 @@ static StringLiteralCheckType checkFormatStringExpr(
59846027
return SLCT_UncheckedLiteral;
59856028

59866029
switch (E->getStmtClass()) {
5987-
case Stmt::InitListExprClass:
5988-
// try to constant-evaluate the string
5989-
break;
6030+
case Stmt::InitListExprClass: {
6031+
const InitListExpr *InitList = cast<InitListExpr>(E);
6032+
// Look through initializers like const char c[] = { "foo" }
6033+
if (InitList->isStringLiteralInit())
6034+
return check(InitList->getInit(0), Offset);
6035+
6036+
return checkEvaluateLiteral(E, Offset);
6037+
}
59906038

59916039
case Stmt::BinaryConditionalOperatorClass:
59926040
case Stmt::ConditionalOperatorClass: {
@@ -6001,8 +6049,7 @@ static StringLiteralCheckType checkFormatStringExpr(
60016049
bool CheckLeft = true, CheckRight = true;
60026050

60036051
bool Cond;
6004-
if (C->getCond()->EvaluateAsBooleanCondition(
6005-
Cond, S.getASTContext(), S.isConstantEvaluatedContext())) {
6052+
if (C->getCond()->EvaluateAsBooleanCondition(Cond, S.getASTContext())) {
60066053
if (Cond)
60076054
CheckRight = false;
60086055
else
@@ -6017,31 +6064,22 @@ static StringLiteralCheckType checkFormatStringExpr(
60176064
if (!CheckLeft)
60186065
Left = SLCT_UncheckedLiteral;
60196066
else {
6020-
Left = checkFormatStringExpr(
6021-
S, ReferenceFormatString, C->getTrueExpr(), Args, APK, format_idx,
6022-
firstDataArg, Type, CallType, InFunctionCall, CheckedVarArgs,
6023-
UncoveredArg, Offset, IgnoreStringsWithoutSpecifiers);
6067+
Left = check(C->getTrueExpr(), Offset);
60246068
if (Left == SLCT_NotALiteral || !CheckRight) {
60256069
return Left;
60266070
}
60276071
}
60286072

6029-
StringLiteralCheckType Right = checkFormatStringExpr(
6030-
S, ReferenceFormatString, C->getFalseExpr(), Args, APK, format_idx,
6031-
firstDataArg, Type, CallType, InFunctionCall, CheckedVarArgs,
6032-
UncoveredArg, Offset, IgnoreStringsWithoutSpecifiers);
6033-
6073+
StringLiteralCheckType Right = check(C->getFalseExpr(), Offset);
60346074
return (CheckLeft && Left < Right) ? Left : Right;
60356075
}
60366076

60376077
case Stmt::ImplicitCastExprClass:
6038-
E = cast<ImplicitCastExpr>(E)->getSubExpr();
6039-
goto tryAgain;
6078+
return check(cast<ImplicitCastExpr>(E)->getSubExpr(), Offset);
60406079

60416080
case Stmt::OpaqueValueExprClass:
60426081
if (const Expr *src = cast<OpaqueValueExpr>(E)->getSourceExpr()) {
6043-
E = src;
6044-
goto tryAgain;
6082+
return check(src, Offset);
60456083
}
60466084
return SLCT_NotALiteral;
60476085

@@ -6072,15 +6110,17 @@ static StringLiteralCheckType checkFormatStringExpr(
60726110
}
60736111

60746112
if (isConstant) {
6075-
if (const Expr *Init = VD->getAnyInitializer()) {
6076-
// Look through initializers like const char c[] = { "foo" }
6077-
if (const InitListExpr *InitList = dyn_cast<InitListExpr>(Init)) {
6078-
if (InitList->isStringLiteralInit())
6079-
Init = InitList->getInit(0)->IgnoreParenImpCasts();
6080-
}
6113+
// If the variable has C++ constant initialization, we need to jump out
6114+
// of format string handling ad-hoc constant evaluation and follow the
6115+
// standard rules.
6116+
const Expr *Init = VD->getInit();
6117+
if (Init && VD->hasConstantInitialization()) {
6118+
return checkEvaluateLiteral(Init, Offset, true);
6119+
}
6120+
Init = VD->getAnyInitializer();
6121+
if (Init) {
60816122
InFunctionCall = false;
6082-
E = Init;
6083-
goto tryAgain;
6123+
return check(Init, Offset);
60846124
}
60856125
}
60866126

@@ -6153,9 +6193,8 @@ static StringLiteralCheckType checkFormatStringExpr(
61536193
}
61546194
return SLCT_UncheckedLiteral;
61556195
}
6156-
E = PVFormatMatches->getFormatString();
61576196
InFunctionCall = false;
6158-
goto tryAgain;
6197+
return check(PVFormatMatches->getFormatString(), Offset);
61596198
}
61606199
}
61616200

@@ -6207,10 +6246,7 @@ static StringLiteralCheckType checkFormatStringExpr(
62076246
StringLiteralCheckType CommonResult;
62086247
for (const auto *FA : ND->specific_attrs<FormatArgAttr>()) {
62096248
const Expr *Arg = CE->getArg(FA->getFormatIdx().getASTIndex());
6210-
StringLiteralCheckType Result = checkFormatStringExpr(
6211-
S, ReferenceFormatString, Arg, Args, APK, format_idx, firstDataArg,
6212-
Type, CallType, InFunctionCall, CheckedVarArgs, UncoveredArg,
6213-
Offset, IgnoreStringsWithoutSpecifiers);
6249+
StringLiteralCheckType Result = check(Arg, Offset);
62146250
if (IsFirst) {
62156251
CommonResult = Result;
62166252
IsFirst = false;
@@ -6223,13 +6259,11 @@ static StringLiteralCheckType checkFormatStringExpr(
62236259
unsigned BuiltinID = FD->getBuiltinID();
62246260
if (BuiltinID == Builtin::BI__builtin___CFStringMakeConstantString ||
62256261
BuiltinID == Builtin::BI__builtin___NSStringMakeConstantString) {
6226-
E = CE->getArg(0);
6227-
goto tryAgain;
6262+
return check(CE->getArg(0), Offset);
62286263
}
62296264
}
62306265
}
6231-
// try to constant-evaluate the string
6232-
break;
6266+
return checkEvaluateLiteral(E, Offset);
62336267
}
62346268
case Stmt::ObjCMessageExprClass: {
62356269
const auto *ME = cast<ObjCMessageExpr>(E);
@@ -6250,8 +6284,7 @@ static StringLiteralCheckType checkFormatStringExpr(
62506284
IgnoreStringsWithoutSpecifiers = true;
62516285
}
62526286

6253-
E = ME->getArg(FA->getFormatIdx().getASTIndex());
6254-
goto tryAgain;
6287+
return check(ME->getArg(FA->getFormatIdx().getASTIndex()), Offset);
62556288
}
62566289
}
62576290

@@ -6302,19 +6335,16 @@ static StringLiteralCheckType checkFormatStringExpr(
63026335
if (LIsInt) {
63036336
if (BinOpKind == BO_Add) {
63046337
sumOffsets(Offset, LResult.Val.getInt(), BinOpKind, RIsInt);
6305-
E = BinOp->getRHS();
6306-
goto tryAgain;
6338+
return check(BinOp->getRHS(), Offset);
63076339
}
63086340
} else {
63096341
sumOffsets(Offset, RResult.Val.getInt(), BinOpKind, RIsInt);
6310-
E = BinOp->getLHS();
6311-
goto tryAgain;
6342+
return check(BinOp->getLHS(), Offset);
63126343
}
63136344
}
63146345
}
63156346

6316-
// try to constant-evaluate the string
6317-
break;
6347+
return checkEvaluateLiteral(E, Offset);
63186348
}
63196349
case Stmt::UnaryOperatorClass: {
63206350
const UnaryOperator *UnaOp = cast<UnaryOperator>(E);
@@ -6326,23 +6356,25 @@ static StringLiteralCheckType checkFormatStringExpr(
63266356
S.isConstantEvaluatedContext())) {
63276357
sumOffsets(Offset, IndexResult.Val.getInt(), BO_Add,
63286358
/*RHS is int*/ true);
6329-
E = ASE->getBase();
6330-
goto tryAgain;
6359+
return check(ASE->getBase(), Offset);
63316360
}
63326361
}
63336362

6334-
// try to constant-evaluate the string
6335-
break;
6363+
return checkEvaluateLiteral(E, Offset);
63366364
}
63376365

63386366
default:
6339-
// try to constant-evaluate the string
6340-
break;
6367+
return checkEvaluateLiteral(E, Offset);
63416368
}
6369+
}
63426370

6371+
StringLiteralCheckType
6372+
FormatStringFinder::checkEvaluateLiteral(const Expr *E, llvm::APSInt Offset,
6373+
bool IsConstantEvaluation) {
63436374
uint64_t EvalOffset = 0;
63446375
const StringLiteral *FakeLiteral = nullptr;
6345-
switch (EvaluateStringAndCreateLiteral(S, E, FakeLiteral, EvalOffset)) {
6376+
switch (EvaluateStringAndCreateLiteral(S, E, IsConstantEvaluation,
6377+
FakeLiteral, EvalOffset)) {
63466378
case SLCER_NotEvaluated:
63476379
return SLCT_NotALiteral;
63486380

@@ -6358,18 +6390,20 @@ static StringLiteralCheckType checkFormatStringExpr(
63586390

63596391
case SLCER_Evaluated:
63606392
InFunctionCall = false;
6361-
E = FakeLiteral;
6362-
Offset = EvalOffset;
6363-
goto tryAgain;
6393+
if (EvalOffset)
6394+
sumOffsets(Offset, llvm::APSInt(64, false) = EvalOffset, BO_Add, true);
6395+
return check(FakeLiteral, Offset);
63646396
}
63656397
}
63666398

6367-
static StringLiteralConstantEvaluationResult
6368-
EvaluateStringAndCreateLiteral(Sema &S, const Expr *E, const StringLiteral *&SL,
6369-
uint64_t &Offset) {
6399+
FormatStringFinder::StringLiteralConstantEvaluationResult
6400+
FormatStringFinder::EvaluateStringAndCreateLiteral(Sema &S, const Expr *E,
6401+
bool IsConstantEvaluation,
6402+
const StringLiteral *&SL,
6403+
uint64_t &Offset) {
63706404
// As a last resort, try to constant-evaluate the format string.
63716405
bool HasNul;
6372-
auto SER = E->tryEvaluateString(S.Context, &HasNul);
6406+
auto SER = E->tryEvaluateString(S.Context, &HasNul, IsConstantEvaluation);
63736407
if (!SER)
63746408
return SLCER_NotEvaluated;
63756409
if (!HasNul)
@@ -6511,11 +6545,9 @@ bool Sema::CheckFormatArguments(ArrayRef<const Expr *> Args,
65116545
// strings by the compiler and thereby (2) can practically remove the source
65126546
// of many format string exploits.
65136547
UncoveredArgHandler UncoveredArg;
6514-
StringLiteralCheckType CT = checkFormatStringExpr(
6515-
*this, ReferenceFormatString, OrigFormatExpr, Args, APK, format_idx,
6516-
firstDataArg, Type, CallType,
6517-
/*IsFunctionCall*/ true, CheckedVarArgs, UncoveredArg,
6518-
/*no string offset*/ llvm::APSInt(64, false) = 0);
6548+
StringLiteralCheckType CT = FormatStringFinder::check(
6549+
*this, ReferenceFormatString, Args, format_idx, firstDataArg, APK, Type,
6550+
CallType, CheckedVarArgs, UncoveredArg);
65196551

65206552
// Generate a diagnostic where an uncovered argument is detected.
65216553
if (UncoveredArg.hasUncoveredArg()) {

0 commit comments

Comments
 (0)