Skip to content

Commit 99fa9a1

Browse files
committed
[clang][CompundLiteralExpr] Don't defer evaluation for CLEs
Previously we would defer evaluation of CLEs until LValue to RValue conversions, which would result in creating values within wrong scope and triggering use-after-frees. This patch instead eagerly evaluates CLEs, within the scope requiring them. This requires storing an extra pointer for CLE expressions with static storage.
1 parent e98a61d commit 99fa9a1

File tree

4 files changed

+58
-8
lines changed

4 files changed

+58
-8
lines changed

clang/include/clang/AST/Expr.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3489,6 +3489,11 @@ class CompoundLiteralExpr : public Expr {
34893489
/// The int part of the pair stores whether this expr is file scope.
34903490
llvm::PointerIntPair<TypeSourceInfo *, 1, bool> TInfoAndScope;
34913491
Stmt *Init;
3492+
3493+
/// Value of constant literals with static storage duration. Used only for
3494+
/// constant folding as CompoundLiteralExpr is not an ICE.
3495+
mutable APValue *StaticValue = nullptr;
3496+
34923497
public:
34933498
CompoundLiteralExpr(SourceLocation lparenloc, TypeSourceInfo *tinfo,
34943499
QualType T, ExprValueKind VK, Expr *init, bool fileScope)
@@ -3518,6 +3523,13 @@ class CompoundLiteralExpr : public Expr {
35183523
TInfoAndScope.setPointer(tinfo);
35193524
}
35203525

3526+
bool hasStaticStorage() const { return isFileScope() && isGLValue(); }
3527+
APValue *getOrCreateStaticValue(ASTContext &Ctx) const;
3528+
APValue &getStaticValue() const {
3529+
assert(StaticValue);
3530+
return *StaticValue;
3531+
}
3532+
35213533
SourceLocation getBeginLoc() const LLVM_READONLY {
35223534
// FIXME: Init should never be null.
35233535
if (!Init)

clang/lib/AST/Expr.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5467,3 +5467,12 @@ ConvertVectorExpr *ConvertVectorExpr::Create(
54675467
return new (Mem) ConvertVectorExpr(SrcExpr, TI, DstType, VK, OK, BuiltinLoc,
54685468
RParenLoc, FPFeatures);
54695469
}
5470+
5471+
APValue *CompoundLiteralExpr::getOrCreateStaticValue(ASTContext &Ctx) const {
5472+
assert(hasStaticStorage());
5473+
if (!StaticValue) {
5474+
StaticValue = new (Ctx) APValue;
5475+
Ctx.addDestruction(StaticValue);
5476+
}
5477+
return StaticValue;
5478+
}

clang/lib/AST/ExprConstant.cpp

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4596,10 +4596,6 @@ handleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, QualType Type,
45964596
return false;
45974597
}
45984598

4599-
APValue Lit;
4600-
if (!Evaluate(Lit, Info, CLE->getInitializer()))
4601-
return false;
4602-
46034599
// According to GCC info page:
46044600
//
46054601
// 6.28 Compound Literals
@@ -4622,7 +4618,12 @@ handleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, QualType Type,
46224618
}
46234619
}
46244620

4625-
CompleteObject LitObj(LVal.Base, &Lit, Base->getType());
4621+
APValue *Lit =
4622+
CLE->hasStaticStorage()
4623+
? &CLE->getStaticValue()
4624+
: Info.CurrentCall->getTemporary(Base, LVal.Base.getVersion());
4625+
4626+
CompleteObject LitObj(LVal.Base, Lit, Base->getType());
46264627
return extractSubobject(Info, Conv, LitObj, LVal.Designator, RVal, AK);
46274628
} else if (isa<StringLiteral>(Base) || isa<PredefinedExpr>(Base)) {
46284629
// Special-case character extraction so we don't have to construct an
@@ -9125,9 +9126,25 @@ bool
91259126
LValueExprEvaluator::VisitCompoundLiteralExpr(const CompoundLiteralExpr *E) {
91269127
assert((!Info.getLangOpts().CPlusPlus || E->isFileScope()) &&
91279128
"lvalue compound literal in c++?");
9128-
// Defer visiting the literal until the lvalue-to-rvalue conversion. We can
9129-
// only see this when folding in C, so there's no standard to follow here.
9130-
return Success(E);
9129+
APValue *Lit;
9130+
// If CompountLiteral has static storage, its value can be used outside
9131+
// this expression. So evaluate it once and store it in ASTContext.
9132+
if (E->hasStaticStorage()) {
9133+
Lit = E->getOrCreateStaticValue(Info.Ctx);
9134+
Result.set(E);
9135+
// Reset any previously evaluated state, otherwise evaluation below might
9136+
// fail.
9137+
// FIXME: Should we just re-use the previously evaluated value instead?
9138+
*Lit = APValue();
9139+
} else {
9140+
Lit = &Info.CurrentCall->createTemporary(E, E->getInitializer()->getType(),
9141+
ScopeKind::FullExpression, Result);
9142+
}
9143+
if (!EvaluateInPlace(*Lit, Info, Result, E->getInitializer())) {
9144+
*Lit = APValue();
9145+
return false;
9146+
}
9147+
return true;
91319148
}
91329149

91339150
bool LValueExprEvaluator::VisitCXXTypeidExpr(const CXXTypeidExpr *E) {
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Test that we can successfully compile this code, especially under ASAN.
2+
// RUN: %clang_cc1 -verify -std=c++20 -fsyntax-only %s
3+
// expected-no-diagnostics
4+
struct Foo {
5+
Foo* f;
6+
operator bool() const { return true; }
7+
};
8+
constexpr Foo f((Foo[]){});
9+
int foo() {
10+
if (Foo(*f.f)) return 1;
11+
return 0;
12+
}

0 commit comments

Comments
 (0)