Skip to content

Commit 40568fe

Browse files
committed
[CodeGen] Emit destructor calls to destruct compound literals
Fix a bug in IRGen where it wasn't destructing compound literals in C that are ObjC pointer arrays or non-trivial structs. Also diagnose jumps that enter or exit the lifetime of the compound literals. rdar://problem/51867864 Differential Revision: https://reviews.llvm.org/D64464
1 parent ddfcda0 commit 40568fe

27 files changed

+460
-28
lines changed

clang/include/clang/AST/ASTImporter.h

+5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include "clang/AST/DeclBase.h"
1818
#include "clang/AST/DeclarationName.h"
19+
#include "clang/AST/ExprCXX.h"
1920
#include "clang/AST/NestedNameSpecifier.h"
2021
#include "clang/AST/TemplateName.h"
2122
#include "clang/AST/Type.h"
@@ -349,6 +350,10 @@ class TypeSourceInfo;
349350
return ToOrErr.takeError();
350351
}
351352

353+
/// Import cleanup objects owned by ExprWithCleanup.
354+
llvm::Expected<ExprWithCleanups::CleanupObject>
355+
Import(ExprWithCleanups::CleanupObject From);
356+
352357
/// Import the given type from the "from" context into the "to"
353358
/// context. A null type is imported as a null type (no error).
354359
///

clang/include/clang/AST/ExprCXX.h

+7-5
Original file line numberDiff line numberDiff line change
@@ -3321,13 +3321,15 @@ class DependentScopeDeclRefExpr final
33213321
/// literal is the extent of the enclosing scope.
33223322
class ExprWithCleanups final
33233323
: public FullExpr,
3324-
private llvm::TrailingObjects<ExprWithCleanups, BlockDecl *> {
3324+
private llvm::TrailingObjects<
3325+
ExprWithCleanups,
3326+
llvm::PointerUnion<BlockDecl *, CompoundLiteralExpr *>> {
33253327
public:
33263328
/// The type of objects that are kept in the cleanup.
3327-
/// It's useful to remember the set of blocks; we could also
3328-
/// remember the set of temporaries, but there's currently
3329-
/// no need.
3330-
using CleanupObject = BlockDecl *;
3329+
/// It's useful to remember the set of blocks and block-scoped compound
3330+
/// literals; we could also remember the set of temporaries, but there's
3331+
/// currently no need.
3332+
using CleanupObject = llvm::PointerUnion<BlockDecl *, CompoundLiteralExpr *>;
33313333

33323334
private:
33333335
friend class ASTStmtReader;

clang/include/clang/AST/TextNodeDumper.h

+1
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ class TextNodeDumper
184184
void dumpBareDeclRef(const Decl *D);
185185
void dumpName(const NamedDecl *ND);
186186
void dumpAccessSpecifier(AccessSpecifier AS);
187+
void dumpCleanupObject(const ExprWithCleanups::CleanupObject &C);
187188

188189
void dumpDeclRef(const Decl *D, StringRef Label = {});
189190

clang/include/clang/Basic/DiagnosticSemaKinds.td

+4
Original file line numberDiff line numberDiff line change
@@ -5543,6 +5543,8 @@ def note_enters_block_captures_weak : Note<
55435543
def note_enters_block_captures_non_trivial_c_struct : Note<
55445544
"jump enters lifetime of block which captures a C struct that is non-trivial "
55455545
"to destroy">;
5546+
def note_enters_compound_literal_scope : Note<
5547+
"jump enters lifetime of a compound literal that is non-trivial to destruct">;
55465548

55475549
def note_exits_cleanup : Note<
55485550
"jump exits scope of variable with __attribute__((cleanup))">;
@@ -5586,6 +5588,8 @@ def note_exits_block_captures_weak : Note<
55865588
def note_exits_block_captures_non_trivial_c_struct : Note<
55875589
"jump exits lifetime of block which captures a C struct that is non-trivial "
55885590
"to destroy">;
5591+
def note_exits_compound_literal_scope : Note<
5592+
"jump exits lifetime of a compound literal that is non-trivial to destruct">;
55895593

55905594
def err_func_returning_qualified_void : ExtWarn<
55915595
"function cannot return qualified void type %0">,

clang/include/clang/Sema/Sema.h

+2-3
Original file line numberDiff line numberDiff line change
@@ -613,9 +613,8 @@ class Sema final {
613613
CleanupInfo Cleanup;
614614

615615
/// ExprCleanupObjects - This is the stack of objects requiring
616-
/// cleanup that are created by the current full expression. The
617-
/// element type here is ExprWithCleanups::Object.
618-
SmallVector<BlockDecl*, 8> ExprCleanupObjects;
616+
/// cleanup that are created by the current full expression.
617+
SmallVector<ExprWithCleanups::CleanupObject, 8> ExprCleanupObjects;
619618

620619
/// Store a set of either DeclRefExprs or MemberExprs that contain a reference
621620
/// to a variable (constant) that may or may not be odr-used in this Expr, and

clang/include/clang/Serialization/ASTBitCodes.h

+3
Original file line numberDiff line numberDiff line change
@@ -1900,6 +1900,9 @@ namespace serialization {
19001900
CTOR_INITIALIZER_INDIRECT_MEMBER
19011901
};
19021902

1903+
/// Kinds of cleanup objects owned by ExprWithCleanups.
1904+
enum CleanupObjectKind { COK_Block, COK_CompoundLiteral };
1905+
19031906
/// Describes the redeclarations of a declaration.
19041907
struct LocalRedeclarationsInfo {
19051908
// The ID of the first declaration

clang/lib/AST/ASTImporter.cpp

+12
Original file line numberDiff line numberDiff line change
@@ -7910,6 +7910,18 @@ void ASTImporter::RegisterImportedDecl(Decl *FromD, Decl *ToD) {
79107910
MapImported(FromD, ToD);
79117911
}
79127912

7913+
llvm::Expected<ExprWithCleanups::CleanupObject>
7914+
ASTImporter::Import(ExprWithCleanups::CleanupObject From) {
7915+
if (auto *CLE = From.dyn_cast<CompoundLiteralExpr *>()) {
7916+
if (Expected<Expr *> R = Import(CLE))
7917+
return ExprWithCleanups::CleanupObject(cast<CompoundLiteralExpr>(*R));
7918+
}
7919+
7920+
// FIXME: Handle BlockDecl when we implement importing BlockExpr in
7921+
// ASTNodeImporter.
7922+
return make_error<ImportError>(ImportError::UnsupportedConstruct);
7923+
}
7924+
79137925
Expected<QualType> ASTImporter::Import(QualType FromT) {
79147926
if (FromT.isNull())
79157927
return QualType{};

clang/lib/AST/JSONNodeDumper.cpp

+10-1
Original file line numberDiff line numberDiff line change
@@ -1334,7 +1334,16 @@ void JSONNodeDumper::VisitExprWithCleanups(const ExprWithCleanups *EWC) {
13341334
if (EWC->getNumObjects()) {
13351335
JOS.attributeArray("cleanups", [this, EWC] {
13361336
for (const ExprWithCleanups::CleanupObject &CO : EWC->getObjects())
1337-
JOS.value(createBareDeclRef(CO));
1337+
if (auto *BD = CO.dyn_cast<BlockDecl *>()) {
1338+
JOS.value(createBareDeclRef(BD));
1339+
} else if (auto *CLE = CO.dyn_cast<CompoundLiteralExpr *>()) {
1340+
llvm::json::Object Obj;
1341+
Obj["id"] = createPointerRepresentation(CLE);
1342+
Obj["kind"] = CLE->getStmtClassName();
1343+
JOS.value(std::move(Obj));
1344+
} else {
1345+
llvm_unreachable("unexpected cleanup object type");
1346+
}
13381347
});
13391348
}
13401349
}

clang/lib/AST/TextNodeDumper.cpp

+18-1
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,23 @@ void TextNodeDumper::dumpAccessSpecifier(AccessSpecifier AS) {
448448
}
449449
}
450450

451+
void TextNodeDumper::dumpCleanupObject(
452+
const ExprWithCleanups::CleanupObject &C) {
453+
if (auto *BD = C.dyn_cast<BlockDecl *>())
454+
dumpDeclRef(BD, "cleanup");
455+
else if (auto *CLE = C.dyn_cast<CompoundLiteralExpr *>())
456+
AddChild([=] {
457+
OS << "cleanup ";
458+
{
459+
ColorScope Color(OS, ShowColors, StmtColor);
460+
OS << CLE->getStmtClassName();
461+
}
462+
dumpPointer(CLE);
463+
});
464+
else
465+
llvm_unreachable("unexpected cleanup type");
466+
}
467+
451468
void TextNodeDumper::dumpDeclRef(const Decl *D, StringRef Label) {
452469
if (!D)
453470
return;
@@ -950,7 +967,7 @@ void TextNodeDumper::VisitMaterializeTemporaryExpr(
950967

951968
void TextNodeDumper::VisitExprWithCleanups(const ExprWithCleanups *Node) {
952969
for (unsigned i = 0, e = Node->getNumObjects(); i != e; ++i)
953-
dumpDeclRef(Node->getObject(i), "cleanup");
970+
dumpCleanupObject(Node->getObject(i));
954971
}
955972

956973
void TextNodeDumper::VisitSizeOfPackExpr(const SizeOfPackExpr *Node) {

clang/lib/CodeGen/CGBlocks.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -860,13 +860,13 @@ static void enterBlockScope(CodeGenFunction &CGF, BlockDecl *block) {
860860
}
861861

862862
/// Enter a full-expression with a non-trivial number of objects to
863-
/// clean up. This is in this file because, at the moment, the only
864-
/// kind of cleanup object is a BlockDecl*.
863+
/// clean up.
865864
void CodeGenFunction::enterNonTrivialFullExpression(const FullExpr *E) {
866865
if (const auto EWC = dyn_cast<ExprWithCleanups>(E)) {
867866
assert(EWC->getNumObjects() != 0);
868867
for (const ExprWithCleanups::CleanupObject &C : EWC->getObjects())
869-
enterBlockScope(*this, C);
868+
if (auto *BD = C.dyn_cast<BlockDecl *>())
869+
enterBlockScope(*this, BD);
870870
}
871871
}
872872

clang/lib/CodeGen/CGExpr.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -4268,6 +4268,14 @@ LValue CodeGenFunction::EmitCompoundLiteralLValue(const CompoundLiteralExpr *E){
42684268
EmitAnyExprToMem(InitExpr, DeclPtr, E->getType().getQualifiers(),
42694269
/*Init*/ true);
42704270

4271+
// Block-scope compound literals are destroyed at the end of the enclosing
4272+
// scope in C.
4273+
if (!getLangOpts().CPlusPlus)
4274+
if (QualType::DestructionKind DtorKind = E->getType().isDestructedType())
4275+
pushLifetimeExtendedDestroy(getCleanupKind(DtorKind), DeclPtr,
4276+
E->getType(), getDestroyer(DtorKind),
4277+
DtorKind & EHCleanup);
4278+
42714279
return Result;
42724280
}
42734281

clang/lib/CodeGen/CGExprAgg.cpp

+14
Original file line numberDiff line numberDiff line change
@@ -659,7 +659,21 @@ AggExprEmitter::VisitCompoundLiteralExpr(CompoundLiteralExpr *E) {
659659
}
660660

661661
AggValueSlot Slot = EnsureSlot(E->getType());
662+
663+
// Block-scope compound literals are destroyed at the end of the enclosing
664+
// scope in C.
665+
bool Destruct =
666+
!CGF.getLangOpts().CPlusPlus && !Slot.isExternallyDestructed();
667+
if (Destruct)
668+
Slot.setExternallyDestructed();
669+
662670
CGF.EmitAggExpr(E->getInitializer(), Slot);
671+
672+
if (Destruct)
673+
if (QualType::DestructionKind DtorKind = E->getType().isDestructedType())
674+
CGF.pushLifetimeExtendedDestroy(
675+
CGF.getCleanupKind(DtorKind), Slot.getAddress(), E->getType(),
676+
CGF.getDestroyer(DtorKind), DtorKind & EHCleanup);
663677
}
664678

665679
/// Attempt to look through various unimportant expressions to find a

clang/lib/CodeGen/CGExprScalar.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,11 @@ class ScalarExprEmitter
556556
Value *VisitMemberExpr(MemberExpr *E);
557557
Value *VisitExtVectorElementExpr(Expr *E) { return EmitLoadOfLValue(E); }
558558
Value *VisitCompoundLiteralExpr(CompoundLiteralExpr *E) {
559+
// Strictly speaking, we shouldn't be calling EmitLoadOfLValue, which
560+
// transitively calls EmitCompoundLiteralLValue, here in C++ since compound
561+
// literals aren't l-values in C++. We do so simply because that's the
562+
// cleanest way to handle compound literals in C++.
563+
// See the discussion here: https://reviews.llvm.org/D64464
559564
return EmitLoadOfLValue(E);
560565
}
561566

clang/lib/Sema/JumpDiagnostics.cpp

+20-5
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ class JumpScopeChecker {
7575
void BuildScopeInformation(Decl *D, unsigned &ParentScope);
7676
void BuildScopeInformation(VarDecl *D, const BlockDecl *BDecl,
7777
unsigned &ParentScope);
78+
void BuildScopeInformation(CompoundLiteralExpr *CLE, unsigned &ParentScope);
7879
void BuildScopeInformation(Stmt *S, unsigned &origParentScope);
7980

8081
void VerifyJumps();
@@ -276,6 +277,16 @@ void JumpScopeChecker::BuildScopeInformation(VarDecl *D,
276277
}
277278
}
278279

280+
/// Build scope information for compound literals of C struct types that are
281+
/// non-trivial to destruct.
282+
void JumpScopeChecker::BuildScopeInformation(CompoundLiteralExpr *CLE,
283+
unsigned &ParentScope) {
284+
unsigned InDiag = diag::note_enters_compound_literal_scope;
285+
unsigned OutDiag = diag::note_exits_compound_literal_scope;
286+
Scopes.push_back(GotoScope(ParentScope, InDiag, OutDiag, CLE->getExprLoc()));
287+
ParentScope = Scopes.size() - 1;
288+
}
289+
279290
/// BuildScopeInformation - The statements from CI to CE are known to form a
280291
/// coherent VLA scope with a specified parent node. Walk through the
281292
/// statements, adding any labels or gotos to LabelAndGotoScopes and recursively
@@ -529,11 +540,15 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S,
529540
// implementable but a lot of work which we haven't felt up to doing.
530541
ExprWithCleanups *EWC = cast<ExprWithCleanups>(S);
531542
for (unsigned i = 0, e = EWC->getNumObjects(); i != e; ++i) {
532-
const BlockDecl *BDecl = EWC->getObject(i);
533-
for (const auto &CI : BDecl->captures()) {
534-
VarDecl *variable = CI.getVariable();
535-
BuildScopeInformation(variable, BDecl, origParentScope);
536-
}
543+
if (auto *BDecl = EWC->getObject(i).dyn_cast<BlockDecl *>())
544+
for (const auto &CI : BDecl->captures()) {
545+
VarDecl *variable = CI.getVariable();
546+
BuildScopeInformation(variable, BDecl, origParentScope);
547+
}
548+
else if (auto *CLE = EWC->getObject(i).dyn_cast<CompoundLiteralExpr *>())
549+
BuildScopeInformation(CLE, origParentScope);
550+
else
551+
llvm_unreachable("unexpected cleanup object type");
537552
}
538553
break;
539554
}

clang/lib/Sema/SemaExpr.cpp

+14-4
Original file line numberDiff line numberDiff line change
@@ -6251,14 +6251,24 @@ Sema::BuildCompoundLiteralExpr(SourceLocation LParenLoc, TypeSourceInfo *TInfo,
62516251
return ExprError();
62526252
}
62536253

6254-
// Compound literals that have automatic storage duration are destroyed at
6255-
// the end of the scope. Emit diagnostics if it is or contains a C union type
6256-
// that is non-trivial to destruct.
6257-
if (!isFileScope)
6254+
if (!isFileScope && !getLangOpts().CPlusPlus) {
6255+
// Compound literals that have automatic storage duration are destroyed at
6256+
// the end of the scope in C; in C++, they're just temporaries.
6257+
6258+
// Emit diagnostics if it is or contains a C union type that is non-trivial
6259+
// to destruct.
62586260
if (E->getType().hasNonTrivialToPrimitiveDestructCUnion())
62596261
checkNonTrivialCUnion(E->getType(), E->getExprLoc(),
62606262
NTCUC_CompoundLiteral, NTCUK_Destruct);
62616263

6264+
// Diagnose jumps that enter or exit the lifetime of the compound literal.
6265+
if (literalType.isDestructedType()) {
6266+
Cleanup.setExprNeedsCleanups(true);
6267+
ExprCleanupObjects.push_back(E);
6268+
getCurFunction()->setHasBranchProtectedScope();
6269+
}
6270+
}
6271+
62626272
if (E->getType().hasNonTrivialToPrimitiveDefaultInitializeCUnion() ||
62636273
E->getType().hasNonTrivialToPrimitiveCopyCUnion())
62646274
checkNonTrivialCUnionInInitializer(E->getInitializer(),

clang/lib/Serialization/ASTReaderStmt.cpp

+11-3
Original file line numberDiff line numberDiff line change
@@ -1819,9 +1819,17 @@ void ASTStmtReader::VisitExprWithCleanups(ExprWithCleanups *E) {
18191819

18201820
unsigned NumObjects = Record.readInt();
18211821
assert(NumObjects == E->getNumObjects());
1822-
for (unsigned i = 0; i != NumObjects; ++i)
1823-
E->getTrailingObjects<BlockDecl *>()[i] =
1824-
readDeclAs<BlockDecl>();
1822+
for (unsigned i = 0; i != NumObjects; ++i) {
1823+
unsigned CleanupKind = Record.readInt();
1824+
ExprWithCleanups::CleanupObject Obj;
1825+
if (CleanupKind == COK_Block)
1826+
Obj = readDeclAs<BlockDecl>();
1827+
else if (CleanupKind == COK_CompoundLiteral)
1828+
Obj = cast<CompoundLiteralExpr>(Record.readSubExpr());
1829+
else
1830+
llvm_unreachable("unexpected cleanup object type");
1831+
E->getTrailingObjects<ExprWithCleanups::CleanupObject>()[i] = Obj;
1832+
}
18251833

18261834
E->ExprWithCleanupsBits.CleanupsHaveSideEffects = Record.readInt();
18271835
E->SubExpr = Record.readSubExpr();

clang/lib/Serialization/ASTWriterStmt.cpp

+9-2
Original file line numberDiff line numberDiff line change
@@ -1721,8 +1721,15 @@ void ASTStmtWriter::VisitCXXPseudoDestructorExpr(CXXPseudoDestructorExpr *E) {
17211721
void ASTStmtWriter::VisitExprWithCleanups(ExprWithCleanups *E) {
17221722
VisitExpr(E);
17231723
Record.push_back(E->getNumObjects());
1724-
for (unsigned i = 0, e = E->getNumObjects(); i != e; ++i)
1725-
Record.AddDeclRef(E->getObject(i));
1724+
for (auto &Obj : E->getObjects()) {
1725+
if (auto *BD = Obj.dyn_cast<BlockDecl *>()) {
1726+
Record.push_back(serialization::COK_Block);
1727+
Record.AddDeclRef(BD);
1728+
} else if (auto *CLE = Obj.dyn_cast<CompoundLiteralExpr *>()) {
1729+
Record.push_back(serialization::COK_CompoundLiteral);
1730+
Record.AddStmt(CLE);
1731+
}
1732+
}
17261733

17271734
Record.push_back(E->cleanupsHaveSideEffects());
17281735
Record.AddStmt(E->getSubExpr());
+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -x objective-c -fobjc-arc -ast-dump=json -ast-dump-filter Test %s | FileCheck %s
2+
3+
typedef struct {
4+
id f;
5+
} S;
6+
7+
id TestCompoundLiteral(id a) {
8+
return ((S){ .f = a }).f;
9+
}
10+
11+
// CHECK: "kind": "ExprWithCleanups",
12+
// CHECK-NEXT: "range": {
13+
// CHECK-NEXT: "begin": {
14+
// CHECK-NEXT: "offset": 202,
15+
// CHECK-NEXT: "col": 10,
16+
// CHECK-NEXT: "tokLen": 1
17+
// CHECK-NEXT: },
18+
// CHECK-NEXT: "end": {
19+
// CHECK-NEXT: "offset": 218,
20+
// CHECK-NEXT: "col": 26,
21+
// CHECK-NEXT: "tokLen": 1
22+
// CHECK-NEXT: }
23+
// CHECK-NEXT: },
24+
// CHECK-NEXT: "type": {
25+
// CHECK-NEXT: "desugaredQualType": "id",
26+
// CHECK-NEXT: "qualType": "id",
27+
// CHECK-NEXT: "typeAliasDeclId": "0x{{.*}}"
28+
// CHECK-NEXT: },
29+
// CHECK-NEXT: "valueCategory": "rvalue",
30+
// CHECK-NEXT: "cleanupsHaveSideEffects": true,
31+
// CHECK-NEXT: "cleanups": [
32+
// CHECK-NEXT: {
33+
// CHECK-NEXT: "id": "0x{{.*}}",
34+
// CHECK-NEXT: "kind": "CompoundLiteralExpr"
35+
// CHECK-NEXT: }
36+
// CHECK-NEXT: ],

clang/test/AST/ast-dump-stmt.m

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %clang_cc1 -Wno-unused -fblocks -fobjc-exceptions -ast-dump -ast-dump-filter Test %s | FileCheck -strict-whitespace %s
1+
// RUN: %clang_cc1 -Wno-unused -fobjc-arc -fblocks -fobjc-exceptions -ast-dump -ast-dump-filter Test %s | FileCheck -strict-whitespace %s
22

33
void TestBlockExpr(int x) {
44
^{ x; };
@@ -34,3 +34,16 @@ void TestObjCAtCatchStmt() {
3434
// CHECK-NEXT: ObjCAtCatchStmt{{.*}} catch all
3535
// CHECK-NEXT: CompoundStmt
3636
// CHECK-NEXT: ObjCAtFinallyStmt
37+
38+
typedef struct {
39+
id f;
40+
} S;
41+
42+
id TestCompoundLiteral(id a) {
43+
return ((S){ .f = a }).f;
44+
}
45+
46+
// CHECK: FunctionDecl{{.*}}TestCompoundLiteral
47+
// CHECK: ExprWithCleanups
48+
// CHECK-NEXT: cleanup CompoundLiteralExpr
49+
// CHECK: CompoundLiteralExpr{{.*}}'S':'S' lvalue

0 commit comments

Comments
 (0)