Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions clang/include/clang/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -3534,6 +3534,10 @@ class CompoundLiteralExpr : public Expr {
/// The int part of the pair stores whether this expr is file scope.
llvm::PointerIntPair<TypeSourceInfo *, 1, bool> TInfoAndScope;
Stmt *Init;

/// Value of constant literals with static storage duration.
mutable APValue *StaticValue = nullptr;

public:
CompoundLiteralExpr(SourceLocation lparenloc, TypeSourceInfo *tinfo,
QualType T, ExprValueKind VK, Expr *init, bool fileScope)
Expand Down Expand Up @@ -3563,6 +3567,10 @@ class CompoundLiteralExpr : public Expr {
TInfoAndScope.setPointer(tinfo);
}

bool hasStaticStorage() const { return isFileScope() && isGLValue(); }
APValue &getOrCreateStaticValue(ASTContext &Ctx) const;
APValue &getStaticValue() const;

SourceLocation getBeginLoc() const LLVM_READONLY {
// FIXME: Init should never be null.
if (!Init)
Expand Down
14 changes: 14 additions & 0 deletions clang/lib/AST/Expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5446,3 +5446,17 @@ ConvertVectorExpr *ConvertVectorExpr::Create(
return new (Mem) ConvertVectorExpr(SrcExpr, TI, DstType, VK, OK, BuiltinLoc,
RParenLoc, FPFeatures);
}

APValue &CompoundLiteralExpr::getOrCreateStaticValue(ASTContext &Ctx) const {
assert(hasStaticStorage());
if (!StaticValue) {
StaticValue = new (Ctx) APValue;
Ctx.addDestruction(StaticValue);
}
return *StaticValue;
}

APValue &CompoundLiteralExpr::getStaticValue() const {
assert(StaticValue);
return *StaticValue;
}
90 changes: 49 additions & 41 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
#include "clang/AST/OptionalDiagnostic.h"
#include "clang/AST/RecordLayout.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/AST/Type.h"
#include "clang/AST/TypeLoc.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/DiagnosticSema.h"
Expand Down Expand Up @@ -4544,6 +4545,30 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,

BaseVal = MTE->getOrCreateValue(false);
assert(BaseVal && "got reference to unevaluated temporary");
} else if (const CompoundLiteralExpr *CLE =
dyn_cast_or_null<CompoundLiteralExpr>(Base)) {
// According to GCC info page:
//
// 6.28 Compound Literals
//
// As an optimization, G++ sometimes gives array compound literals
// longer lifetimes: when the array either appears outside a function or
// has a const-qualified type. If foo and its initializer had elements
// of type char *const rather than char *, or if foo were a global
// variable, the array would have static storage duration. But it is
// probably safest just to avoid the use of array compound literals in
// C++ code.
//
// Obey that rule by checking constness for converted array types.
if (QualType CLETy = CLE->getType(); CLETy->isArrayType() &&
!LValType->isArrayType() &&
!CLETy.isConstant(Info.Ctx)) {
Info.FFDiag(E);
Info.Note(CLE->getExprLoc(), diag::note_declared_at);
return CompleteObject();
}

BaseVal = &CLE->getStaticValue();
} else {
if (!IsAccess)
return CompleteObject(LVal.getLValueBase(), nullptr, BaseType);
Expand Down Expand Up @@ -4609,44 +4634,7 @@ handleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, QualType Type,
WantObjectRepresentation ? AK_ReadObjectRepresentation : AK_Read;

if (Base && !LVal.getLValueCallIndex() && !Type.isVolatileQualified()) {
if (const CompoundLiteralExpr *CLE = dyn_cast<CompoundLiteralExpr>(Base)) {
// In C99, a CompoundLiteralExpr is an lvalue, and we defer evaluating the
// initializer until now for such expressions. Such an expression can't be
// an ICE in C, so this only matters for fold.
if (Type.isVolatileQualified()) {
Info.FFDiag(Conv);
return false;
}

APValue Lit;
if (!Evaluate(Lit, Info, CLE->getInitializer()))
return false;

// According to GCC info page:
//
// 6.28 Compound Literals
//
// As an optimization, G++ sometimes gives array compound literals longer
// lifetimes: when the array either appears outside a function or has a
// const-qualified type. If foo and its initializer had elements of type
// char *const rather than char *, or if foo were a global variable, the
// array would have static storage duration. But it is probably safest
// just to avoid the use of array compound literals in C++ code.
//
// Obey that rule by checking constness for converted array types.

QualType CLETy = CLE->getType();
if (CLETy->isArrayType() && !Type->isArrayType()) {
if (!CLETy.isConstant(Info.Ctx)) {
Info.FFDiag(Conv);
Info.Note(CLE->getExprLoc(), diag::note_declared_at);
return false;
}
}

CompleteObject LitObj(LVal.Base, &Lit, Base->getType());
return extractSubobject(Info, Conv, LitObj, LVal.Designator, RVal, AK);
} else if (isa<StringLiteral>(Base) || isa<PredefinedExpr>(Base)) {
if (isa<StringLiteral>(Base) || isa<PredefinedExpr>(Base)) {
// Special-case character extraction so we don't have to construct an
// APValue for the whole string.
assert(LVal.Designator.Entries.size() <= 1 &&
Expand Down Expand Up @@ -9159,9 +9147,29 @@ bool
LValueExprEvaluator::VisitCompoundLiteralExpr(const CompoundLiteralExpr *E) {
assert((!Info.getLangOpts().CPlusPlus || E->isFileScope()) &&
"lvalue compound literal in c++?");
// Defer visiting the literal until the lvalue-to-rvalue conversion. We can
// only see this when folding in C, so there's no standard to follow here.
return Success(E);
APValue *Lit;
// If CompountLiteral has static storage, its value can be used outside
// this expression. So evaluate it once and store it in ASTContext.
if (E->hasStaticStorage()) {
Lit = &E->getOrCreateStaticValue(Info.Ctx);
Result.set(E);
// Reset any previously evaluated state, otherwise evaluation below might
// fail.
// FIXME: Should we just re-use the previously evaluated value instead?
*Lit = APValue();
} else {
assert(!Info.getLangOpts().CPlusPlus);
Lit = &Info.CurrentCall->createTemporary(E, E->getInitializer()->getType(),
ScopeKind::Block, Result);
}
// FIXME: Evaluating in place isn't always right. We should figure out how to
// use appropriate evaluation context here, see
// clang/test/AST/static-compound-literals-reeval.cpp for a failure.
if (!EvaluateInPlace(*Lit, Info, Result, E->getInitializer())) {
*Lit = APValue();
return false;
}
return true;
}

bool LValueExprEvaluator::VisitCXXTypeidExpr(const CXXTypeidExpr *E) {
Expand Down
17 changes: 17 additions & 0 deletions clang/test/AST/static-compound-literals-crash.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// FIXME: These test cases currently crash during codegen, despite initializers
// for CLEs being constant.
// RUN: not --crash %clang_cc1 -verify -std=c++20 -emit-llvm %s
// expected-no-diagnostics
namespace case1 {
struct RR { int&& r; };
struct Z { RR* x; };
constinit Z z = { (RR[1]){1} };
}


namespace case2 {
struct RR { int r; };
struct Z { int x; const RR* y; int z; };
inline int f() { return 0; }
Z z2 = { 10, (const RR[1]){__builtin_constant_p(z2.x)}, z2.y->r+f() };
}
9 changes: 9 additions & 0 deletions clang/test/AST/static-compound-literals-reeval.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Test that we can successfully compile this code, especially under ASAN.
// RUN: %clang_cc1 -emit-llvm -std=c++20 %s -o- | FileCheck %s
struct RR { int r; };
struct Z { int x; const RR* y; int z; };
constinit Z z = { 10, (const RR[1]){__builtin_constant_p(z.x)}, z.y->r };
// Check that we zero-initialize z.y->r.
// CHECK: @.compoundliteral = internal constant [1 x %struct.RR] zeroinitializer
// FIXME: Despite of z.y->r being 0, we evaluate z.z to 1.
// CHECK: @z = global %struct.Z { i32 10, ptr @.compoundliteral, i32 1 }
12 changes: 12 additions & 0 deletions clang/test/AST/static-compound-literals.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Test that we can successfully compile this code, especially under ASAN.
// RUN: %clang_cc1 -verify -std=c++20 -fsyntax-only %s
// expected-no-diagnostics
struct Foo {
Foo* f;
operator bool() const { return true; }
};
constexpr Foo f((Foo[]){});
int foo() {
if (Foo(*f.f)) return 1;
return 0;
}
Loading