Index: include/clang/AST/ASTImporter.h =================================================================== --- include/clang/AST/ASTImporter.h +++ include/clang/AST/ASTImporter.h @@ -16,6 +16,7 @@ #include "clang/AST/DeclBase.h" #include "clang/AST/DeclarationName.h" +#include "clang/AST/ExprCXX.h" #include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/TemplateName.h" #include "clang/AST/Type.h" @@ -339,6 +340,10 @@ return ToOrErr.takeError(); } + /// Import cleanup objects owned by ExprWithCleanup. + llvm::Expected + Import(ExprWithCleanups::CleanupObject From); + /// Import the given type from the "from" context into the "to" /// context. A null type is imported as a null type (no error). /// Index: include/clang/AST/ExprCXX.h =================================================================== --- include/clang/AST/ExprCXX.h +++ include/clang/AST/ExprCXX.h @@ -3211,13 +3211,14 @@ /// literal is the extent of the enclosing scope. class ExprWithCleanups final : public FullExpr, - private llvm::TrailingObjects { + private llvm::TrailingObjects< + ExprWithCleanups, + llvm::PointerUnion> { public: /// The type of objects that are kept in the cleanup. - /// It's useful to remember the set of blocks; we could also - /// remember the set of temporaries, but there's currently - /// no need. - using CleanupObject = BlockDecl *; + /// It's useful to remember the set of blocks and compound literals; we could + /// also remember the set of temporaries, but there's currently no need. + using CleanupObject = llvm::PointerUnion; private: friend class ASTStmtReader; Index: include/clang/AST/TextNodeDumper.h =================================================================== --- include/clang/AST/TextNodeDumper.h +++ include/clang/AST/TextNodeDumper.h @@ -186,6 +186,7 @@ void dumpBareDeclRef(const Decl *D); void dumpName(const NamedDecl *ND); void dumpAccessSpecifier(AccessSpecifier AS); + void dumpCleanupObject(const ExprWithCleanups::CleanupObject &C); void dumpDeclRef(const Decl *D, StringRef Label = {}); Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -5167,6 +5167,8 @@ def note_enters_block_captures_non_trivial_c_struct : Note< "jump enters lifetime of block which captures a C struct that is non-trivial " "to destroy">; +def note_enters_compound_literal_scope : Note< + "jump enters lifetime of a compound literal that is non-trivial to destruct">; def note_exits_cleanup : Note< "jump exits scope of variable with __attribute__((cleanup))">; @@ -5210,6 +5212,8 @@ def note_exits_block_captures_non_trivial_c_struct : Note< "jump exits lifetime of block which captures a C struct that is non-trivial " "to destroy">; +def note_exits_compound_literal_scope : Note< + "jump exits lifetime of a compound literal that is non-trivial to destruct">; def err_func_returning_qualified_void : ExtWarn< "function cannot return qualified void type %0">, Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -585,7 +585,7 @@ /// ExprCleanupObjects - This is the stack of objects requiring /// cleanup that are created by the current full expression. The /// element type here is ExprWithCleanups::Object. - SmallVector ExprCleanupObjects; + SmallVector ExprCleanupObjects; /// Store a set of either DeclRefExprs or MemberExprs that contain a reference /// to a variable (constant) that may or may not be odr-used in this Expr, and Index: include/clang/Serialization/ASTBitCodes.h =================================================================== --- include/clang/Serialization/ASTBitCodes.h +++ include/clang/Serialization/ASTBitCodes.h @@ -2014,6 +2014,12 @@ CTOR_INITIALIZER_INDIRECT_MEMBER }; + /// Kinds of cleanup objects owned by ExprWithCleanups. + enum CleanupObjectKind { + COK_Block, + COK_CompoundLiteral + }; + /// Describes the redeclarations of a declaration. struct LocalRedeclarationsInfo { // The ID of the first declaration Index: lib/AST/ASTImporter.cpp =================================================================== --- lib/AST/ASTImporter.cpp +++ lib/AST/ASTImporter.cpp @@ -7852,6 +7852,19 @@ MapImported(FromD, ToD); } +llvm::Expected +ASTImporter::Import(ExprWithCleanups::CleanupObject From) { + if (auto *BD = From.dyn_cast()) { + if (Expected R = Import(BD)) + return ExprWithCleanups::CleanupObject(cast(*R)); + } else if (auto *CLE = From.dyn_cast()) { + if (Expected R = Import(CLE)) + return ExprWithCleanups::CleanupObject(cast(*R)); + } + + return nullptr; +} + Expected ASTImporter::Import(QualType FromT) { if (FromT.isNull()) return QualType{}; Index: lib/AST/JSONNodeDumper.cpp =================================================================== --- lib/AST/JSONNodeDumper.cpp +++ lib/AST/JSONNodeDumper.cpp @@ -1288,7 +1288,16 @@ if (EWC->getNumObjects()) { JOS.attributeArray("cleanups", [this, EWC] { for (const ExprWithCleanups::CleanupObject &CO : EWC->getObjects()) - JOS.value(createBareDeclRef(CO)); + if (auto *BD = CO.dyn_cast()) { + JOS.value(createBareDeclRef(BD)); + } else if (auto *CLE = CO.dyn_cast()) { + llvm::json::Object Obj; + Obj["id"] = createPointerRepresentation(CLE); + Obj["kind"] = CLE->getStmtClassName(); + JOS.value(std::move(Obj)); + } else { + llvm_unreachable("unexpected cleanup object type"); + } }); } } Index: lib/AST/TextNodeDumper.cpp =================================================================== --- lib/AST/TextNodeDumper.cpp +++ lib/AST/TextNodeDumper.cpp @@ -448,6 +448,23 @@ } } +void TextNodeDumper::dumpCleanupObject( + const ExprWithCleanups::CleanupObject &C) { + if (auto *BD = C.dyn_cast()) + dumpDeclRef(BD, "cleanup"); + else if (auto *CLE = C.dyn_cast()) + AddChild([=] { + OS << "cleanup "; + { + ColorScope Color(OS, ShowColors, StmtColor); + OS << CLE->getStmtClassName(); + } + dumpPointer(CLE); + }); + else + llvm_unreachable("unexpected cleanup type"); +} + void TextNodeDumper::dumpDeclRef(const Decl *D, StringRef Label) { if (!D) return; @@ -947,7 +964,7 @@ void TextNodeDumper::VisitExprWithCleanups(const ExprWithCleanups *Node) { for (unsigned i = 0, e = Node->getNumObjects(); i != e; ++i) - dumpDeclRef(Node->getObject(i), "cleanup"); + dumpCleanupObject(Node->getObject(i)); } void TextNodeDumper::VisitSizeOfPackExpr(const SizeOfPackExpr *Node) { Index: lib/CodeGen/CGBlocks.cpp =================================================================== --- lib/CodeGen/CGBlocks.cpp +++ lib/CodeGen/CGBlocks.cpp @@ -863,9 +863,19 @@ /// kind of cleanup object is a BlockDecl*. void CodeGenFunction::enterNonTrivialFullExpression(const FullExpr *E) { if (const auto EWC = dyn_cast(E)) { + auto *DomInst = Builder.CreateAlignedLoad( + Int8Ty, llvm::Constant::getNullValue(Int8PtrTy), + CharUnits::One()); + DominatingIPs.push_back(DomInst); + assert(EWC->getNumObjects() != 0); for (const ExprWithCleanups::CleanupObject &C : EWC->getObjects()) - enterBlockScope(*this, C); + if (auto *BD = C.dyn_cast()) + enterBlockScope(*this, BD); + else if (auto *CLE = C.dyn_cast()) + enterCompoundLiteralScope(CLE, DomInst); + else + llvm_unreachable("unexpected cleanup object type"); } } Index: lib/CodeGen/CGCleanup.cpp =================================================================== --- lib/CodeGen/CGCleanup.cpp +++ lib/CodeGen/CGCleanup.cpp @@ -1275,3 +1275,21 @@ pushDestroy(NormalAndEHCleanup, Ptr, TempType, destroyCXXObject, /*useEHCleanup*/ true); } + +void CodeGenFunction::enterCompoundLiteralScope(const CompoundLiteralExpr *CLE, + llvm::Instruction *DomInst) { + // The lifetime of a compound literal with automatic storage duration ends at + // the end of its enclosing block. Push an inactive cleanup here so that its + // destructor is called when the lifetime ends. + QualType Ty = CLE->getType(); + QualType::DestructionKind DtorKind = Ty.isDestructedType(); + assert(DtorKind == QualType::DK_nontrivial_c_struct && + "non-trivial C struct type expected here"); + bool UseArrayEHCleanup = needsEHCleanup(DtorKind); + CleanupKind CK = UseArrayEHCleanup ? + InactiveNormalAndEHCleanup : InactiveNormalCleanup; + pushCompoundLiteralCleanup(CK, CLE, getDestroyer(DtorKind), + UseArrayEHCleanup); + CompoundLiteralCleanupInfoMap[CLE].setCleanup( + EHStack.stable_begin(), DomInst); +} Index: lib/CodeGen/CGDecl.cpp =================================================================== --- lib/CodeGen/CGDecl.cpp +++ lib/CodeGen/CGDecl.cpp @@ -536,6 +536,28 @@ } }; + /// A cleanup for destructing compound literals that have automatic storage + /// duration and are of C struct types that are non-trivial to destruct. + struct DestroyCompoundLiteral final : EHScopeStack::Cleanup { + DestroyCompoundLiteral(const CompoundLiteralExpr *CLE, + CodeGenFunction::Destroyer *destroyer, + bool useEHCleanupForArray) + : CLE(CLE), destroyer(destroyer), + useEHCleanupForArray(useEHCleanupForArray) {} + + const CompoundLiteralExpr *CLE; + CodeGenFunction::Destroyer *destroyer; + bool useEHCleanupForArray; + + void Emit(CodeGenFunction &CGF, Flags flags) override { + // Don't use an EH cleanup recursively from an EH cleanup. + bool useEHCleanupForArray = + flags.isForNormalCleanup() && this->useEHCleanupForArray; + CGF.emitDestroy(CGF.CompoundLiteralCleanupInfoMap[CLE].getAddr(), + CLE->getType(), destroyer, useEHCleanupForArray); + } + }; + struct CallStackRestore final : EHScopeStack::Cleanup { Address Stack; CallStackRestore(Address Stack) : Stack(Stack) {} @@ -2072,6 +2094,14 @@ destroyer, useEHCleanupForArray); } +void CodeGenFunction::pushCompoundLiteralCleanup(CleanupKind cleanupKind, + const CompoundLiteralExpr *CLE, + Destroyer *destroyer, + bool useEHCleanupForArray) { + pushFullExprCleanup(cleanupKind, CLE, destroyer, + useEHCleanupForArray); +} + void CodeGenFunction::pushStackRestore(CleanupKind Kind, Address SPMem) { EHStack.pushCleanup(Kind, SPMem); } Index: lib/CodeGen/CGExpr.cpp =================================================================== --- lib/CodeGen/CGExpr.cpp +++ lib/CodeGen/CGExpr.cpp @@ -4151,6 +4151,15 @@ EmitAnyExprToMem(InitExpr, DeclPtr, E->getType().getQualifiers(), /*Init*/ true); + // If the compound literal has a non-trivial C struct type, activate the + // cleanup that was pushed at the entry of the full expression and record the + // address of the aggregate. + if (E->getType().isDestructedType() == QualType::DK_nontrivial_c_struct) { + CompoundLiteralCleanupInfo &Info = CompoundLiteralCleanupInfoMap[E]; + ActivateCleanupBlock(Info.Cleanup, Info.DominatingIP); + Info.setAddr(DeclPtr); + } + return Result; } @@ -4633,6 +4642,10 @@ LValue CodeGenFunction::EmitCallExprLValue(const CallExpr *E) { RValue RV = EmitCallExpr(E); + if (E->getType().isDestructedType() == QualType::DK_nontrivial_c_struct) + pushDestroy(QualType::DK_nontrivial_c_struct, RV.getAggregateAddress(), + E->getType()); + if (!RV.isScalar()) return MakeAddrLValue(RV.getAggregateAddress(), E->getType(), AlignmentSource::Decl); Index: lib/CodeGen/CGExprAgg.cpp =================================================================== --- lib/CodeGen/CGExprAgg.cpp +++ lib/CodeGen/CGExprAgg.cpp @@ -245,7 +245,7 @@ const Expr *E, llvm::function_ref EmitCall) { QualType RetTy = E->getType(); bool RequiresDestruction = - Dest.isIgnored() && + !Dest.isExternallyDestructed() && RetTy.isDestructedType() == QualType::DK_nontrivial_c_struct; // If it makes no observable difference, save a memcpy + temporary. @@ -657,6 +657,18 @@ AggValueSlot Slot = EnsureSlot(E->getType()); CGF.EmitAggExpr(E->getInitializer(), Slot); + + // If the compound literal has a non-trivial C struct type, activate the + // cleanup that was pushed at the entry of the full expression and record the + // address of the aggregate. + if (!Slot.isExternallyDestructed() && + E->getType().isDestructedType() == QualType::DK_nontrivial_c_struct) { + CodeGenFunction::CompoundLiteralCleanupInfo &Info = + CGF.CompoundLiteralCleanupInfoMap[E]; + CGF.ActivateCleanupBlock(Info.Cleanup, Info.DominatingIP); + Info.setAddr(Slot.getAddress()); + Slot.setExternallyDestructed(); + } } /// Attempt to look through various unimportant expressions to find a @@ -811,7 +823,16 @@ // into existence. if (E->getSubExpr()->getType().isVolatileQualified()) { EnsureDest(E->getType()); - return Visit(E->getSubExpr()); + Visit(E->getSubExpr()); + + if (!Dest.isExternallyDestructed() && + E->getType().isDestructedType() == QualType::DK_nontrivial_c_struct) { + CGF.pushDestroy(QualType::DK_nontrivial_c_struct, Dest.getAddress(), + E->getType()); + Dest.setExternallyDestructed(); + } + + return; } LLVM_FALLTHROUGH; Index: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ lib/CodeGen/CodeGenFunction.h @@ -342,6 +342,10 @@ /// we prefer to insert allocas. llvm::AssertingVH AllocaInsertPt; + /// List of dominating IPs. These instructions are deleted after IR generation + /// of a function is completed. + SmallVector DominatingIPs; + /// API for captured statement code generation. class CGCapturedStmtInfo { public: @@ -559,6 +563,31 @@ llvm::BasicBlock *getInvokeDestImpl(); + /// This struct has the information that is needed to destruct a compound + /// literal with automatic storage duration that has a C struct type that is + /// non-trivial to destruct. + struct CompoundLiteralCleanupInfo { + EHScopeStack::stable_iterator Cleanup; + llvm::Instruction *DominatingIP; + llvm::Value *AddrPtr; + CharUnits AddrAlignment; + + void setCleanup(EHScopeStack::stable_iterator C, llvm::Instruction *D) { + Cleanup = C; + DominatingIP = D; + } + void setAddr(Address Addr) { + AddrPtr = Addr.getPointer(); + AddrAlignment = Addr.getAlignment(); + } + Address getAddr() const { + return Address(AddrPtr, AddrAlignment); + } + }; + + llvm::DenseMap + CompoundLiteralCleanupInfoMap; + template typename DominatingValue::saved_type saveValueInCond(T value) { return DominatingValue::save(*this, value); @@ -1685,6 +1714,10 @@ Address addr, QualType type); void pushDestroy(CleanupKind kind, Address addr, QualType type, Destroyer *destroyer, bool useEHCleanupForArray); + void pushCompoundLiteralCleanup(CleanupKind kind, + const CompoundLiteralExpr *CLE, + Destroyer *destroyer, + bool useEHCleanupForArray); void pushLifetimeExtendedDestroy(CleanupKind kind, Address addr, QualType type, Destroyer *destroyer, bool useEHCleanupForArray); @@ -3997,6 +4030,9 @@ } void enterNonTrivialFullExpression(const FullExpr *E); + void enterCompoundLiteralScope(const CompoundLiteralExpr *CLE, + llvm::Instruction *DomInst); + void EmitCXXThrowExpr(const CXXThrowExpr *E, bool KeepInsertionPoint = true); RValue EmitAtomicExpr(AtomicExpr *E); Index: lib/CodeGen/CodeGenFunction.cpp =================================================================== --- lib/CodeGen/CodeGenFunction.cpp +++ lib/CodeGen/CodeGenFunction.cpp @@ -389,6 +389,9 @@ AllocaInsertPt = nullptr; Ptr->eraseFromParent(); + for (auto I : DominatingIPs) + I->eraseFromParent(); + // If someone took the address of a label but never did an indirect goto, we // made a zero entry PHI node, which is illegal, zap it now. if (IndirectBranch) { Index: lib/Sema/JumpDiagnostics.cpp =================================================================== --- lib/Sema/JumpDiagnostics.cpp +++ lib/Sema/JumpDiagnostics.cpp @@ -75,6 +75,7 @@ void BuildScopeInformation(Decl *D, unsigned &ParentScope); void BuildScopeInformation(VarDecl *D, const BlockDecl *BDecl, unsigned &ParentScope); + void BuildScopeInformation(CompoundLiteralExpr *CLE, unsigned &ParentScope); void BuildScopeInformation(Stmt *S, unsigned &origParentScope); void VerifyJumps(); @@ -276,6 +277,16 @@ } } +/// Build scope information for compound literals of C struct types that are +/// non-trivial to destruct. +void JumpScopeChecker::BuildScopeInformation(CompoundLiteralExpr *CLE, + unsigned &ParentScope) { + unsigned InDiag = diag::note_enters_compound_literal_scope; + unsigned OutDiag = diag::note_exits_compound_literal_scope; + Scopes.push_back(GotoScope(ParentScope, InDiag, OutDiag, CLE->getExprLoc())); + ParentScope = Scopes.size() - 1; +} + /// BuildScopeInformation - The statements from CI to CE are known to form a /// coherent VLA scope with a specified parent node. Walk through the /// statements, adding any labels or gotos to LabelAndGotoScopes and recursively @@ -529,11 +540,15 @@ // implementable but a lot of work which we haven't felt up to doing. ExprWithCleanups *EWC = cast(S); for (unsigned i = 0, e = EWC->getNumObjects(); i != e; ++i) { - const BlockDecl *BDecl = EWC->getObject(i); - for (const auto &CI : BDecl->captures()) { - VarDecl *variable = CI.getVariable(); - BuildScopeInformation(variable, BDecl, origParentScope); - } + if (auto *BDecl = EWC->getObject(i).dyn_cast()) + for (const auto &CI : BDecl->captures()) { + VarDecl *variable = CI.getVariable(); + BuildScopeInformation(variable, BDecl, origParentScope); + } + else if (auto *CLE = EWC->getObject(i).dyn_cast()) + BuildScopeInformation(CLE, origParentScope); + else + llvm_unreachable("unexpected cleanup object type"); } break; } Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -635,6 +635,10 @@ if (E->getType().getObjCLifetime() == Qualifiers::OCL_Weak) Cleanup.setExprNeedsCleanups(true); + if (E->getType().isVolatileQualified() && + E->getType().isDestructedType() == QualType::DK_nontrivial_c_struct) + Cleanup.setExprNeedsCleanups(true); + // C++ [conv.lval]p3: // If T is cv std::nullptr_t, the result is a null pointer constant. CastKind CK = T->isNullPtrType() ? CK_NullToPointer : CK_LValueToRValue; @@ -5569,6 +5573,10 @@ } } + if (Call.get()->getType().isDestructedType() == + QualType::DK_nontrivial_c_struct) + Cleanup.setExprNeedsCleanups(true); + return Call; } @@ -6076,7 +6084,7 @@ ILE->setInit(i, ConstantExpr::Create(Context, Init)); } - Expr *E = new (Context) CompoundLiteralExpr(LParenLoc, TInfo, literalType, + auto *E = new (Context) CompoundLiteralExpr(LParenLoc, TInfo, literalType, VK, LiteralExpr, isFileScope); if (isFileScope) { if (!LiteralExpr->isTypeDependent() && @@ -6094,6 +6102,13 @@ return ExprError(); } + if (!isFileScope && + literalType.isDestructedType() == QualType::DK_nontrivial_c_struct) { + Cleanup.setExprNeedsCleanups(true); + ExprCleanupObjects.push_back(E); + getCurFunction()->setHasBranchProtectedScope(); + } + return MaybeBindToTemporary(E); } Index: lib/Serialization/ASTReaderStmt.cpp =================================================================== --- lib/Serialization/ASTReaderStmt.cpp +++ lib/Serialization/ASTReaderStmt.cpp @@ -1655,9 +1655,17 @@ unsigned NumObjects = Record.readInt(); assert(NumObjects == E->getNumObjects()); - for (unsigned i = 0; i != NumObjects; ++i) - E->getTrailingObjects()[i] = - ReadDeclAs(); + for (unsigned i = 0; i != NumObjects; ++i) { + unsigned CleanupKind = Record.readInt(); + ExprWithCleanups::CleanupObject Obj; + if (CleanupKind == COK_Block) + Obj = ReadDeclAs(); + else if (CleanupKind == COK_CompoundLiteral) + Obj = cast(Record.readSubExpr()); + else + llvm_unreachable("unexpected cleanup object type"); + E->getTrailingObjects()[i] = Obj; + } E->ExprWithCleanupsBits.CleanupsHaveSideEffects = Record.readInt(); E->SubExpr = Record.readSubExpr(); Index: lib/Serialization/ASTWriterStmt.cpp =================================================================== --- lib/Serialization/ASTWriterStmt.cpp +++ lib/Serialization/ASTWriterStmt.cpp @@ -1598,8 +1598,15 @@ void ASTStmtWriter::VisitExprWithCleanups(ExprWithCleanups *E) { VisitExpr(E); Record.push_back(E->getNumObjects()); - for (unsigned i = 0, e = E->getNumObjects(); i != e; ++i) - Record.AddDeclRef(E->getObject(i)); + for (auto &Obj : E->getObjects()) { + if (auto *BD = Obj.dyn_cast()) { + Record.push_back(serialization::COK_Block); + Record.AddDeclRef(BD); + } else if (auto *CLE = Obj.dyn_cast()) { + Record.push_back(serialization::COK_CompoundLiteral); + Record.AddStmt(CLE); + } + } Record.push_back(E->cleanupsHaveSideEffects()); Record.AddStmt(E->getSubExpr()); Index: test/AST/ast-dump-objc-arc-json.m =================================================================== --- /dev/null +++ test/AST/ast-dump-objc-arc-json.m @@ -0,0 +1,34 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -x objective-c -fobjc-arc -ast-dump=json -ast-dump-filter Test %s | FileCheck %s + +typedef struct { + id f; +} S; + +id TestCompoundLiteral(id a) { + return ((S){ .f = a }).f; +} + +// CHECK: "kind": "ExprWithCleanups", +// CHECK-NEXT: "range": { +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "col": 10, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "col": 26, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: "type": { +// CHECK-NEXT: "desugaredQualType": "id", +// CHECK-NEXT: "qualType": "id", +// CHECK-NEXT: "typeAliasDeclId": +// CHECK-NEXT: }, +// CHECK-NEXT: "valueCategory": "rvalue", +// CHECK-NEXT: "cleanupsHaveSideEffects": true, +// CHECK-NEXT: "cleanups": [ +// CHECK-NEXT: { +// CHECK-NEXT: "id": "0x{{.*}}", +// CHECK-NEXT: "kind": "CompoundLiteralExpr" +// CHECK-NEXT: } +// CHECK-NEXT: ], Index: test/AST/ast-dump-stmt.m =================================================================== --- test/AST/ast-dump-stmt.m +++ test/AST/ast-dump-stmt.m @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -Wno-unused -fblocks -fobjc-exceptions -ast-dump -ast-dump-filter Test %s | FileCheck -strict-whitespace %s +// RUN: %clang_cc1 -Wno-unused -fobjc-arc -fblocks -fobjc-exceptions -ast-dump -ast-dump-filter Test %s | FileCheck -strict-whitespace %s void TestBlockExpr(int x) { ^{ x; }; @@ -34,3 +34,16 @@ // CHECK-NEXT: ObjCAtCatchStmt{{.*}} catch all // CHECK-NEXT: CompoundStmt // CHECK-NEXT: ObjCAtFinallyStmt + +typedef struct { + id f; +} S; + +id TestCompoundLiteral(id a) { + return ((S){ .f = a }).f; +} + +// CHECK: FunctionDecl{{.*}}TestCompoundLiteral +// CHECK: ExprWithCleanups +// CHECK-NEXT: cleanup CompoundLiteralExpr +// CHECK: CompoundLiteralExpr{{.*}}'S':'S' lvalue Index: test/CodeGenObjC/arc.m =================================================================== --- test/CodeGenObjC/arc.m +++ test/CodeGenObjC/arc.m @@ -1536,12 +1536,13 @@ // CHECK-LABEL: define void @test71 void test71(void) { - // FIXME: It would be nice if the __destructor_8_s40 for the first call (and - // the following lifetime.end) came before the second call. - // // CHECK: %[[T:[^ ]+]] = bitcast %struct.AggDtor* %[[TMP1:[^ ]+]] to i8* // CHECK: call void @llvm.lifetime.start.p0i8({{[^,]+}}, i8* %[[T]]) // CHECK: call void @getAggDtor(%struct.AggDtor* sret %[[TMP1]]) + // CHECK: %[[T:[^ ]+]] = bitcast %struct.AggDtor* %[[TMP1]] to i8** + // CHECK: call void @__destructor_8_s40(i8** %[[T]]) + // CHECK: %[[T:[^ ]+]] = bitcast %struct.AggDtor* %[[TMP1:[^ ]+]] to i8* + // CHECK: call void @llvm.lifetime.end.p0i8({{[^,]+}}, i8* %[[T]]) // CHECK: %[[T:[^ ]+]] = bitcast %struct.AggDtor* %[[TMP2:[^ ]+]] to i8* // CHECK: call void @llvm.lifetime.start.p0i8({{[^,]+}}, i8* %[[T]]) // CHECK: call void @getAggDtor(%struct.AggDtor* sret %[[TMP2]]) @@ -1549,10 +1550,6 @@ // CHECK: call void @__destructor_8_s40(i8** %[[T]]) // CHECK: %[[T:[^ ]+]] = bitcast %struct.AggDtor* %[[TMP2:[^ ]+]] to i8* // CHECK: call void @llvm.lifetime.end.p0i8({{[^,]+}}, i8* %[[T]]) - // CHECK: %[[T:[^ ]+]] = bitcast %struct.AggDtor* %[[TMP1]] to i8** - // CHECK: call void @__destructor_8_s40(i8** %[[T]]) - // CHECK: %[[T:[^ ]+]] = bitcast %struct.AggDtor* %[[TMP1:[^ ]+]] to i8* - // CHECK: call void @llvm.lifetime.end.p0i8({{[^,]+}}, i8* %[[T]]) getAggDtor(); getAggDtor(); } Index: test/CodeGenObjC/strong-in-c-struct.m =================================================================== --- test/CodeGenObjC/strong-in-c-struct.m +++ test/CodeGenObjC/strong-in-c-struct.m @@ -521,7 +521,9 @@ // CHECK: define void @test_copy_constructor_StrongVolatile0( // CHECK: call void @__copy_constructor_8_8_t0w4_sv8( +// CHECK-NOT: call // CHECK: call void @__destructor_8_sv8( +// CHECK-NOT: call // CHECK: define linkonce_odr hidden void @__copy_constructor_8_8_t0w4_sv8( // CHECK: %[[V8:.*]] = load volatile i8*, i8** %{{.*}}, align 8 @@ -718,4 +720,89 @@ VolatileArray t = *a; } +// CHECK: define void @test_member_access( +// CHECK: %[[TMP:.*]] = alloca %[[STRUCT_STRONGSMALL]], +// CHECK: %[[V3:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[TMP]] to i8** +// CHECK: call void @__destructor_8_s8(i8** %[[V3]]) +// CHECK: call void @func( + +void test_member_access(void) { + id t = getStrongSmall().f1; + func(0); +} + +// CHECK: define void @test_compound_literal( +// CHECK: %[[_COMPOUNDLITERAL:.*]] = alloca %[[STRUCT_STRONGSMALL]], +// CHECK: %[[CLEANUP_ISACTIVE:.*]] = alloca i1, +// CHECK: %[[_COMPOUNDLITERAL1:.*]] = alloca %[[STRUCT_STRONGSMALL]], +// CHECK: %[[CLEANUP_ISACTIVE4:.*]] = alloca i1, +// CHECK: store i1 false, i1* %[[CLEANUP_ISACTIVE]], +// CHECK: store i1 false, i1* %[[CLEANUP_ISACTIVE4]], + +// CHECK: store i1 true, i1* %[[CLEANUP_ISACTIVE]], + +// CHECK: store i1 true, i1* %[[CLEANUP_ISACTIVE4]], + +// CHECK: call void @func( +// CHECK: %[[IS_ACTIVE4:.*]] = load i1, i1* %[[CLEANUP_ISACTIVE4]], +// CHECK: br i1 %[[IS_ACTIVE4]], + +// CHECK: %[[V1:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL1]] to i8** +// CHECK: call void @__destructor_8_s8(i8** %[[V1]]) + +// CHECK: %[[IS_ACTIVE:.*]] = load i1, i1* %[[CLEANUP_ISACTIVE]], +// CHECK: br i1 %[[IS_ACTIVE]], + +// CHECK: %[[V2:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL]] to i8** +// CHECK: call void @__destructor_8_s8(i8** %[[V2]]) + +void test_compound_literal(int c) { + StrongSmall *p = c ? &(StrongSmall){ 1, 0 } : &(StrongSmall){ 2, 0 }; + func(0); +} + +// CHECK: define void @test_compound_literal2( +// CHECK: %[[AGG_TMP_ENSURED:.*]] = alloca %[[STRUCT_STRONGSMALL]], +// CHECK: %[[CLEANUP_ISACTIVE:.*]] = alloca i1, +// CHECK: %[[AGG_TMP_ENSURED1:.*]] = alloca %[[STRUCT_STRONGSMALL]], +// CHECK: %[[CLEANUP_ISACTIVE4:.*]] = alloca i1, +// CHECK: store i1 false, i1* %[[CLEANUP_ISACTIVE]], +// CHECK: store i1 false, i1* %[[CLEANUP_ISACTIVE4]], + +// CHECK: store i1 true, i1* %[[CLEANUP_ISACTIVE]], + +// CHECK: store i1 true, i1* %[[CLEANUP_ISACTIVE4]], + +// CHECK: call void @func( +// CHECK: %[[IS_ACTIVE4:.*]] = load i1, i1* %[[CLEANUP_ISACTIVE4]], +// CHECK: br i1 %[[IS_ACTIVE4]], + +// CHECK: %[[V1:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[AGG_TMP_ENSURED1]] to i8** +// CHECK: call void @__destructor_8_s8(i8** %[[V1]]) + +// CHECK: %[[IS_ACTIVE:.*]] = load i1, i1* %[[CLEANUP_ISACTIVE]], +// CHECK: br i1 %[[IS_ACTIVE]], + +// CHECK: %[[V2:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[AGG_TMP_ENSURED]] to i8** +// CHECK: call void @__destructor_8_s8(i8** %[[V2]]) + +void test_compound_literal2(int c) { + c ? (StrongSmall){ 1, 0 } : (StrongSmall){ 2, 0 }; + func(0); +} + +// CHECK: define void @test_volatile_variable_reference( +// CHECK: %[[AGG_TMP_ENSURED:.*]] = alloca %[[STRUCT_STRONGSMALL]], +// CHECK: %[[V1:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[AGG_TMP_ENSURED]] to i8** +// CHECK: %[[V2:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %{{.*}} to i8** +// CHECK: call void @__copy_constructor_8_8_tv0w32_sv8(i8** %[[V1]], i8** %[[V2]]) +// CHECK: %[[V3:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[AGG_TMP_ENSURED]] to i8** +// CHECK: call void @__destructor_8_s8(i8** %[[V3]]) +// CHECK: call void @func( + +void test_volatile_variable_reference(volatile StrongSmall *a) { + (void)*a; + func(0); +} + #endif /* USESTRUCT */ Index: test/Import/objc-arc/Inputs/cleanup-objects.m =================================================================== --- /dev/null +++ test/Import/objc-arc/Inputs/cleanup-objects.m @@ -0,0 +1,10 @@ +typedef struct { + id x; +} S; + +id getObj(int c, id a) { + // Commenting out the following line because AST importer crashes when trying + // to import a BlockExpr. + // return c ? ^{ return a; }() : ((S){ .x = a }).x; + return ((S){ .x = a }).x; +} Index: test/Import/objc-arc/test-cleanup-object.m =================================================================== --- /dev/null +++ test/Import/objc-arc/test-cleanup-object.m @@ -0,0 +1,10 @@ +// RUN: clang-import-test -x objective-c -objc-arc -import %S/Inputs/cleanup-objects.m -dump-ast -expression %s | FileCheck %s + +// CHECK: FunctionDecl {{.*}} getObj ' +// CHECK: ExprWithCleanups +// CHECK-NEXT: cleanup CompoundLiteralExpr + +void test(int c, id a) { + (void)getObj(c, a); +} + Index: test/PCH/non-trivial-c-compound-literal.m =================================================================== --- /dev/null +++ test/PCH/non-trivial-c-compound-literal.m @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -x objective-c -fobjc-arc -emit-pch -o %t %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -x objective-c -fobjc-arc -include-pch %t -emit-llvm -o - %s | FileCheck %s + +#ifndef HEADER +#define HEADER + +typedef struct { + id f; +} S; + +static inline id getObj(id a) { + S *p = &(S){ .f = a }; + return p->f; +} + +#else + +// CHECK: %[[STRUCT_S:.*]] = type { i8* } + +// CHECK: define internal i8* @getObj( +// CHECK: %[[_COMPOUNDLITERAL:.*]] = alloca %[[STRUCT_S]], +// CHECK: %[[V5:.*]] = bitcast %[[STRUCT_S]]* %[[_COMPOUNDLITERAL]] to i8** +// CHECK: call void @__destructor_8_s0(i8** %[[V5]]) + +id test(id a) { + return getObj(a); +} + +#endif Index: test/SemaObjC/strong-in-c-struct.m =================================================================== --- test/SemaObjC/strong-in-c-struct.m +++ test/SemaObjC/strong-in-c-struct.m @@ -54,3 +54,21 @@ func(^{ func2(x); }); goto *ips; // expected-error {{cannot jump}} } + +void test_compound_literal0(int cond, id x) { + switch (cond) { + case 0: + (void)(Strong){ .a = x }; // expected-note {{jump enters lifetime of a compound literal that is non-trivial to destruct}} + break; + default: // expected-error {{cannot jump from switch statement to this case label}} + break; + } +} + +void test_compound_literal1(id x) { + static void *ips[] = { &&L0 }; +L0: // expected-note {{possible target of indirect goto}} + ; + (void)(Strong){ .a = x }; // expected-note {{jump exits lifetime of a compound literal that is non-trivial to destruct}} + goto *ips; // expected-error {{cannot jump}} +} Index: tools/clang-import-test/clang-import-test.cpp =================================================================== --- tools/clang-import-test/clang-import-test.cpp +++ tools/clang-import-test/clang-import-test.cpp @@ -64,6 +64,10 @@ llvm::cl::desc("The language to parse (default: c++)"), llvm::cl::init("c++")); +static llvm::cl::opt + ObjCARC("objc-arc", llvm::cl::init(false), + llvm::cl::desc("Emable ObjC ARC")); + static llvm::cl::opt DumpAST("dump-ast", llvm::cl::init(false), llvm::cl::desc("Dump combined AST")); @@ -185,6 +189,8 @@ Inv->getLangOpts()->ObjC = 1; } } + Inv->getLangOpts()->ObjCAutoRefCount = ObjCARC; + Inv->getLangOpts()->Bool = true; Inv->getLangOpts()->WChar = true; Inv->getLangOpts()->Blocks = true;