summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAkira Hatanaka <ahatanaka@apple.com>2018-09-08 20:03:00 +0000
committerAkira Hatanaka <ahatanaka@apple.com>2018-09-08 20:03:00 +0000
commitc500c067925f3be65c645c21ff369e0f8137d0d8 (patch)
tree640cd0bb22e8f38ddaef038453d3dc9cf4e63604
parentbfe49d565c4fe4c87c797bf4319e7e673ef4dbb2 (diff)
Distinguish `__block` variables that are captured by escaping blocks
from those that aren't. This patch changes the way __block variables that aren't captured by escaping blocks are handled: - Since non-escaping blocks on the stack never get copied to the heap (see https://reviews.llvm.org/D49303), Sema shouldn't error out when the type of a non-escaping __block variable doesn't have an accessible copy constructor. - IRGen doesn't have to use the specialized byref structure (see https://clang.llvm.org/docs/Block-ABI-Apple.html#id8) for a non-escaping __block variable anymore. Instead IRGen can emit the variable as a normal variable and copy the reference to the block literal. Byref copy/dispose helpers aren't needed either. rdar://problem/39352313 Differential Revision: https://reviews.llvm.org/D51564 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@341754 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/clang/AST/Decl.h23
-rw-r--r--include/clang/Sema/ScopeInfo.h17
-rw-r--r--lib/AST/Decl.cpp8
-rw-r--r--lib/CodeGen/CGBlocks.cpp28
-rw-r--r--lib/CodeGen/CGClass.cpp2
-rw-r--r--lib/CodeGen/CGDecl.cpp19
-rw-r--r--lib/CodeGen/CGExpr.cpp4
-rw-r--r--lib/CodeGen/CodeGenFunction.h11
-rw-r--r--lib/Sema/ScopeInfo.cpp2
-rw-r--r--lib/Sema/Sema.cpp54
-rw-r--r--lib/Sema/SemaDecl.cpp33
-rw-r--r--lib/Sema/SemaExpr.cpp3
-rw-r--r--lib/Serialization/ASTReaderDecl.cpp1
-rw-r--r--lib/Serialization/ASTWriterDecl.cpp3
-rw-r--r--test/CodeGen/block-byref-aggr.c4
-rw-r--r--test/CodeGen/blocks-seq.c1
-rw-r--r--test/CodeGen/exceptions.c1
-rw-r--r--test/CodeGen/personality.c1
-rw-r--r--test/CodeGenCXX/block-capture.cpp2
-rw-r--r--test/CodeGenCXX/blocks.cpp1
-rw-r--r--test/CodeGenCXX/debug-info-blocks.cpp1
-rw-r--r--test/CodeGenCXX/noescape.cpp31
-rw-r--r--test/CodeGenObjC/arc-no-arc-exceptions.m1
-rw-r--r--test/CodeGenObjC/arc-unoptimized-byref-var.m1
-rw-r--r--test/CodeGenObjC/blocks-1.m49
-rw-r--r--test/CodeGenObjC/noescape.m52
-rw-r--r--test/CodeGenObjCXX/arc-blocks.mm11
-rw-r--r--test/PCH/block-helpers.cpp20
-rw-r--r--test/SemaObjCXX/blocks.mm8
-rw-r--r--test/SemaObjCXX/noescape.mm25
30 files changed, 335 insertions, 82 deletions
diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h
index 0148762b3b..596236ed08 100644
--- a/include/clang/AST/Decl.h
+++ b/include/clang/AST/Decl.h
@@ -965,6 +965,8 @@ protected:
/// Defines kind of the ImplicitParamDecl: 'this', 'self', 'vtt', '_cmd' or
/// something else.
unsigned ImplicitParamKind : 3;
+
+ unsigned EscapingByref : 1;
};
union {
@@ -1407,6 +1409,19 @@ public:
NonParmVarDeclBits.PreviousDeclInSameBlockScope = Same;
}
+ /// Indicates the capture is a __block variable that is captured by a block
+ /// that can potentially escape (a block for which BlockDecl::doesNotEscape
+ /// returns false).
+ bool isEscapingByref() const;
+
+ /// Indicates the capture is a __block variable that is never captured by an
+ /// escaping block.
+ bool isNonEscapingByref() const;
+
+ void setEscapingByref() {
+ NonParmVarDeclBits.EscapingByref = true;
+ }
+
/// Retrieve the variable declaration from which this variable could
/// be instantiated, if it is an instantiation (rather than a non-template).
VarDecl *getTemplateInstantiationPattern() const;
@@ -3858,6 +3873,14 @@ public:
/// variable.
bool isByRef() const { return VariableAndFlags.getInt() & flag_isByRef; }
+ bool isEscapingByref() const {
+ return getVariable()->isEscapingByref();
+ }
+
+ bool isNonEscapingByref() const {
+ return getVariable()->isNonEscapingByref();
+ }
+
/// Whether this is a nested capture, i.e. the variable captured
/// is not from outside the immediately enclosing function/block.
bool isNested() const { return VariableAndFlags.getInt() & flag_isNested; }
diff --git a/include/clang/Sema/ScopeInfo.h b/include/clang/Sema/ScopeInfo.h
index 5925fd6cce..e09a68aba7 100644
--- a/include/clang/Sema/ScopeInfo.h
+++ b/include/clang/Sema/ScopeInfo.h
@@ -31,6 +31,7 @@
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
+#include "llvm/ADT/TinyPtrVector.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/ErrorHandling.h"
#include <algorithm>
@@ -202,6 +203,12 @@ public:
/// function.
SmallVector<CompoundScopeInfo, 4> CompoundScopes;
+ /// The set of blocks that are introduced in this function.
+ llvm::SmallPtrSet<const BlockDecl *, 1> Blocks;
+
+ /// The set of __block variables that are introduced in this function.
+ llvm::TinyPtrVector<VarDecl *> ByrefBlockVars;
+
/// A list of PartialDiagnostics created but delayed within the
/// current function scope. These diagnostics are vetted for reachability
/// prior to being emitted.
@@ -426,6 +433,16 @@ public:
(HasBranchProtectedScope && HasBranchIntoScope));
}
+ // Add a block introduced in this function.
+ void addBlock(const BlockDecl *BD) {
+ Blocks.insert(BD);
+ }
+
+ // Add a __block variable introduced in this function.
+ void addByrefBlockVar(VarDecl *VD) {
+ ByrefBlockVars.push_back(VD);
+ }
+
bool isCoroutine() const { return !FirstCoroutineStmtLoc.isInvalid(); }
void setFirstCoroutineStmt(SourceLocation Loc, StringRef Keyword) {
diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp
index 272e49a799..5a64c67f9b 100644
--- a/lib/AST/Decl.cpp
+++ b/lib/AST/Decl.cpp
@@ -2361,6 +2361,14 @@ static DeclT *getDefinitionOrSelf(DeclT *D) {
return D;
}
+bool VarDecl::isEscapingByref() const {
+ return hasAttr<BlocksAttr>() && NonParmVarDeclBits.EscapingByref;
+}
+
+bool VarDecl::isNonEscapingByref() const {
+ return hasAttr<BlocksAttr>() && !NonParmVarDeclBits.EscapingByref;
+}
+
VarDecl *VarDecl::getTemplateInstantiationPattern() const {
// If it's a variable template specialization, find the template or partial
// specialization from which it was instantiated.
diff --git a/lib/CodeGen/CGBlocks.cpp b/lib/CodeGen/CGBlocks.cpp
index 170e4f0c08..507ffec74c 100644
--- a/lib/CodeGen/CGBlocks.cpp
+++ b/lib/CodeGen/CGBlocks.cpp
@@ -493,7 +493,11 @@ static QualType getCaptureFieldType(const CodeGenFunction &CGF,
return CGF.BlockInfo->getCapture(VD).fieldType();
if (auto *FD = CGF.LambdaCaptureFields.lookup(VD))
return FD->getType();
- return VD->getType();
+ // If the captured variable is a non-escaping __block variable, the field
+ // type is the reference type. If the variable is a __block variable that
+ // already has a reference type, the field type is the variable's type.
+ return VD->isNonEscapingByref() ?
+ CGF.getContext().getLValueReferenceType(VD->getType()) : VD->getType();
}
/// Compute the layout of the given block. Attempts to lay the block
@@ -549,7 +553,7 @@ static void computeBlockInfo(CodeGenModule &CGM, CodeGenFunction *CGF,
for (const auto &CI : block->captures()) {
const VarDecl *variable = CI.getVariable();
- if (CI.isByRef()) {
+ if (CI.isEscapingByref()) {
// We have to copy/dispose of the __block reference.
info.NeedsCopyDispose = true;
@@ -1032,7 +1036,7 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) {
// The lambda capture in a lambda's conversion-to-block-pointer is
// special; we'll simply emit it directly.
src = Address::invalid();
- } else if (CI.isByRef()) {
+ } else if (CI.isEscapingByref()) {
if (BlockInfo && CI.isNested()) {
// We need to use the capture from the enclosing block.
const CGBlockInfo::Capture &enclosingCapture =
@@ -1060,7 +1064,7 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) {
// the block field. There's no need to chase the forwarding
// pointer at this point, since we're building something that will
// live a shorter life than the stack byref anyway.
- if (CI.isByRef()) {
+ if (CI.isEscapingByref()) {
// Get a void* that points to the byref struct.
llvm::Value *byrefPointer;
if (CI.isNested())
@@ -1279,8 +1283,7 @@ RValue CodeGenFunction::EmitBlockCallExpr(const CallExpr *E,
return EmitCall(FnInfo, Callee, ReturnValue, Args);
}
-Address CodeGenFunction::GetAddrOfBlockDecl(const VarDecl *variable,
- bool isByRef) {
+Address CodeGenFunction::GetAddrOfBlockDecl(const VarDecl *variable) {
assert(BlockInfo && "evaluating block ref without block information?");
const CGBlockInfo::Capture &capture = BlockInfo->getCapture(variable);
@@ -1291,7 +1294,7 @@ Address CodeGenFunction::GetAddrOfBlockDecl(const VarDecl *variable,
Builder.CreateStructGEP(LoadBlockStruct(), capture.getIndex(),
capture.getOffset(), "block.capture.addr");
- if (isByRef) {
+ if (variable->isEscapingByref()) {
// addr should be a void** right now. Load, then cast the result
// to byref*.
@@ -1305,6 +1308,10 @@ Address CodeGenFunction::GetAddrOfBlockDecl(const VarDecl *variable,
variable->getName());
}
+ assert((!variable->isNonEscapingByref() ||
+ capture.fieldType()->isReferenceType()) &&
+ "the capture field of a non-escaping variable should have a "
+ "reference type");
if (capture.fieldType()->isReferenceType())
addr = EmitLoadOfReference(MakeAddrLValue(addr, capture.fieldType()));
@@ -1656,7 +1663,7 @@ computeCopyInfoForBlockCapture(const BlockDecl::Capture &CI, QualType T,
return std::make_pair(BlockCaptureEntityKind::CXXRecord, BlockFieldFlags());
}
BlockFieldFlags Flags;
- if (CI.isByRef()) {
+ if (CI.isEscapingByref()) {
Flags = BLOCK_FIELD_IS_BYREF;
if (T.isObjCGCWeak())
Flags |= BLOCK_FIELD_IS_WEAK;
@@ -2102,7 +2109,7 @@ getBlockFieldFlagsForObjCObjectPointer(const BlockDecl::Capture &CI,
static std::pair<BlockCaptureEntityKind, BlockFieldFlags>
computeDestroyInfoForBlockCapture(const BlockDecl::Capture &CI, QualType T,
const LangOptions &LangOpts) {
- if (CI.isByRef()) {
+ if (CI.isEscapingByref()) {
BlockFieldFlags Flags = BLOCK_FIELD_IS_BYREF;
if (T.isObjCGCWeak())
Flags |= BLOCK_FIELD_IS_WEAK;
@@ -2564,6 +2571,9 @@ BlockByrefHelpers *
CodeGenFunction::buildByrefHelpers(llvm::StructType &byrefType,
const AutoVarEmission &emission) {
const VarDecl &var = *emission.Variable;
+ assert(var.isEscapingByref() &&
+ "only escaping __block variables need byref helpers");
+
QualType type = var.getType();
auto &byrefInfo = getBlockByrefInfo(&var);
diff --git a/lib/CodeGen/CGClass.cpp b/lib/CodeGen/CGClass.cpp
index 468d81cbbb..aca0fd6f6e 100644
--- a/lib/CodeGen/CGClass.cpp
+++ b/lib/CodeGen/CGClass.cpp
@@ -2839,7 +2839,7 @@ void CodeGenFunction::EmitLambdaBlockInvokeBody() {
CallArgList CallArgs;
QualType ThisType = getContext().getPointerType(getContext().getRecordType(Lambda));
- Address ThisPtr = GetAddrOfBlockDecl(variable, false);
+ Address ThisPtr = GetAddrOfBlockDecl(variable);
CallArgs.add(RValue::get(ThisPtr.getPointer()), ThisType);
// Add the rest of the parameters.
diff --git a/lib/CodeGen/CGDecl.cpp b/lib/CodeGen/CGDecl.cpp
index 5c47212f1b..d31802b6a4 100644
--- a/lib/CodeGen/CGDecl.cpp
+++ b/lib/CodeGen/CGDecl.cpp
@@ -1212,8 +1212,8 @@ CodeGenFunction::EmitAutoVarAlloca(const VarDecl &D) {
AutoVarEmission emission(D);
- bool isByRef = D.hasAttr<BlocksAttr>();
- emission.IsByRef = isByRef;
+ bool isEscapingByRef = D.isEscapingByref();
+ emission.IsEscapingByRef = isEscapingByRef;
CharUnits alignment = getContext().getDeclAlign(&D);
@@ -1252,8 +1252,8 @@ CodeGenFunction::EmitAutoVarAlloca(const VarDecl &D) {
// in OpenCL.
if ((!getLangOpts().OpenCL ||
Ty.getAddressSpace() == LangAS::opencl_constant) &&
- (CGM.getCodeGenOpts().MergeAllConstants && !NRVO && !isByRef &&
- CGM.isTypeConstant(Ty, true))) {
+ (CGM.getCodeGenOpts().MergeAllConstants && !NRVO &&
+ !isEscapingByRef && CGM.isTypeConstant(Ty, true))) {
EmitStaticVarDecl(D, llvm::GlobalValue::InternalLinkage);
// Signal this condition to later callbacks.
@@ -1305,7 +1305,7 @@ CodeGenFunction::EmitAutoVarAlloca(const VarDecl &D) {
} else {
CharUnits allocaAlignment;
llvm::Type *allocaTy;
- if (isByRef) {
+ if (isEscapingByRef) {
auto &byrefInfo = getBlockByrefInfo(&D);
allocaTy = byrefInfo.Type;
allocaAlignment = byrefInfo.ByrefAlignment;
@@ -1505,7 +1505,7 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) {
}
// Initialize the structure of a __block variable.
- if (emission.IsByRef)
+ if (emission.IsEscapingByRef)
emitByrefStructureInit(emission);
// Initialize the variable here if it doesn't have a initializer and it is a
@@ -1515,7 +1515,7 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) {
type.isNonTrivialToPrimitiveDefaultInitialize() ==
QualType::PDIK_Struct) {
LValue Dst = MakeAddrLValue(emission.getAllocatedAddress(), type);
- if (emission.IsByRef)
+ if (emission.IsEscapingByRef)
drillIntoBlockVariable(*this, Dst, &D);
defaultInitNonTrivialCStructVar(Dst);
return;
@@ -1527,7 +1527,7 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) {
// Check whether this is a byref variable that's potentially
// captured and moved by its own initializer. If so, we'll need to
// emit the initializer first, then copy into the variable.
- bool capturedByInit = emission.IsByRef && isCapturedBy(D, Init);
+ bool capturedByInit = emission.IsEscapingByRef && isCapturedBy(D, Init);
Address Loc =
capturedByInit ? emission.Addr : emission.getObjectAddress(*this);
@@ -1721,7 +1721,8 @@ void CodeGenFunction::EmitAutoVarCleanups(const AutoVarEmission &emission) {
// If this is a block variable, call _Block_object_destroy
// (on the unforwarded address). Don't enter this cleanup if we're in pure-GC
// mode.
- if (emission.IsByRef && CGM.getLangOpts().getGC() != LangOptions::GCOnly) {
+ if (emission.IsEscapingByRef &&
+ CGM.getLangOpts().getGC() != LangOptions::GCOnly) {
BlockFieldFlags Flags = BLOCK_FIELD_IS_BYREF;
if (emission.Variable->getType().isObjCGCWeak())
Flags |= BLOCK_FIELD_IS_WEAK;
diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp
index b74937e9ca..028aa963cd 100644
--- a/lib/CodeGen/CGExpr.cpp
+++ b/lib/CodeGen/CGExpr.cpp
@@ -2486,7 +2486,7 @@ LValue CodeGenFunction::EmitDeclRefLValue(const DeclRefExpr *E) {
}
assert(isa<BlockDecl>(CurCodeDecl));
- Address addr = GetAddrOfBlockDecl(VD, VD->hasAttr<BlocksAttr>());
+ Address addr = GetAddrOfBlockDecl(VD);
return MakeAddrLValue(addr, T, AlignmentSource::Decl);
}
}
@@ -2538,7 +2538,7 @@ LValue CodeGenFunction::EmitDeclRefLValue(const DeclRefExpr *E) {
}
// Drill into block byref variables.
- bool isBlockByref = VD->hasAttr<BlocksAttr>();
+ bool isBlockByref = VD->isEscapingByref();
if (isBlockByref) {
addr = emitBlockByrefAddress(addr, VD);
}
diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h
index f56284d4d3..0a11d045f7 100644
--- a/lib/CodeGen/CodeGenFunction.h
+++ b/lib/CodeGen/CodeGenFunction.h
@@ -1787,7 +1787,7 @@ public:
llvm::Value *ptr);
Address LoadBlockStruct();
- Address GetAddrOfBlockDecl(const VarDecl *var, bool ByRef);
+ Address GetAddrOfBlockDecl(const VarDecl *var);
/// BuildBlockByrefAddress - Computes the location of the
/// data in a variable which is declared as __block.
@@ -2683,8 +2683,9 @@ public:
llvm::Value *NRVOFlag;
- /// True if the variable is a __block variable.
- bool IsByRef;
+ /// True if the variable is a __block variable that is captured by an
+ /// escaping block.
+ bool IsEscapingByRef;
/// True if the variable is of aggregate type and has a constant
/// initializer.
@@ -2704,7 +2705,7 @@ public:
AutoVarEmission(const VarDecl &variable)
: Variable(&variable), Addr(Address::invalid()), NRVOFlag(nullptr),
- IsByRef(false), IsConstantAggregate(false),
+ IsEscapingByRef(false), IsConstantAggregate(false),
SizeForLifetimeMarkers(nullptr), AllocaAddr(Address::invalid()) {}
bool wasEmittedAsGlobal() const { return !Addr.isValid(); }
@@ -2734,7 +2735,7 @@ public:
/// Note that this does not chase the forwarding pointer for
/// __block decls.
Address getObjectAddress(CodeGenFunction &CGF) const {
- if (!IsByRef) return Addr;
+ if (!IsEscapingByRef) return Addr;
return CGF.emitBlockByrefAddress(Addr, Variable, /*forward*/ false);
}
diff --git a/lib/Sema/ScopeInfo.cpp b/lib/Sema/ScopeInfo.cpp
index 62a83ccb70..bd8db6f4ed 100644
--- a/lib/Sema/ScopeInfo.cpp
+++ b/lib/Sema/ScopeInfo.cpp
@@ -54,6 +54,8 @@ void FunctionScopeInfo::Clear() {
PossiblyUnreachableDiags.clear();
WeakObjectUses.clear();
ModifiedNonNullParams.clear();
+ Blocks.clear();
+ ByrefBlockVars.clear();
}
static const NamedDecl *getBestPropertyDecl(const ObjCPropertyRefExpr *PropE) {
diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp
index 82a04e1070..0af6d0d89c 100644
--- a/lib/Sema/Sema.cpp
+++ b/lib/Sema/Sema.cpp
@@ -1398,11 +1398,65 @@ void Sema::RecordParsingTemplateParameterDepth(unsigned Depth) {
"Remove assertion if intentionally called in a non-lambda context.");
}
+// Check that the type of the VarDecl has an accessible copy constructor and
+// resolve its destructor's exception spefication.
+static void checkEscapingByref(VarDecl *VD, Sema &S) {
+ QualType T = VD->getType();
+ EnterExpressionEvaluationContext scope(
+ S, Sema::ExpressionEvaluationContext::PotentiallyEvaluated);
+ SourceLocation Loc = VD->getLocation();
+ Expr *VarRef = new (S.Context) DeclRefExpr(VD, false, T, VK_LValue, Loc);
+ ExprResult Result = S.PerformMoveOrCopyInitialization(
+ InitializedEntity::InitializeBlock(Loc, T, false), VD, VD->getType(),
+ VarRef, /*AllowNRVO=*/true);
+ if (!Result.isInvalid()) {
+ Result = S.MaybeCreateExprWithCleanups(Result);
+ Expr *Init = Result.getAs<Expr>();
+ S.Context.setBlockVarCopyInit(VD, Init, S.canThrow(Init));
+ }
+
+ // The destructor's exception spefication is needed when IRGen generates
+ // block copy/destroy functions. Resolve it here.
+ if (const CXXRecordDecl *RD = T->getAsCXXRecordDecl())
+ if (CXXDestructorDecl *DD = RD->getDestructor()) {
+ auto *FPT = DD->getType()->getAs<FunctionProtoType>();
+ S.ResolveExceptionSpec(Loc, FPT);
+ }
+}
+
+static void markEscapingByrefs(const FunctionScopeInfo &FSI, Sema &S) {
+ // Set the EscapingByref flag of __block variables captured by
+ // escaping blocks.
+ for (const BlockDecl *BD : FSI.Blocks) {
+ if (BD->doesNotEscape())
+ continue;
+ for (const BlockDecl::Capture &BC : BD->captures()) {
+ VarDecl *VD = BC.getVariable();
+ if (VD->hasAttr<BlocksAttr>())
+ VD->setEscapingByref();
+ }
+ }
+
+ for (VarDecl *VD : FSI.ByrefBlockVars) {
+ // __block variables might require us to capture a copy-initializer.
+ if (!VD->isEscapingByref())
+ continue;
+ // It's currently invalid to ever have a __block variable with an
+ // array type; should we diagnose that here?
+ // Regardless, we don't want to ignore array nesting when
+ // constructing this copy.
+ if (VD->getType()->isStructureOrClassType())
+ checkEscapingByref(VD, S);
+ }
+}
+
void Sema::PopFunctionScopeInfo(const AnalysisBasedWarnings::Policy *WP,
const Decl *D, const BlockExpr *blkExpr) {
assert(!FunctionScopes.empty() && "mismatched push/pop!");
FunctionScopeInfo *Scope = FunctionScopes.pop_back_val();
+ markEscapingByrefs(*Scope, *this);
+
if (LangOpts.OpenMP)
popOpenMPFunctionRegion(Scope);
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index c705b4920e..ab5f6883b2 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -11790,37 +11790,8 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
QualType type = var->getType();
if (type->isDependentType()) return;
- // __block variables might require us to capture a copy-initializer.
- if (var->hasAttr<BlocksAttr>()) {
- // It's currently invalid to ever have a __block variable with an
- // array type; should we diagnose that here?
-
- // Regardless, we don't want to ignore array nesting when
- // constructing this copy.
- if (type->isStructureOrClassType()) {
- EnterExpressionEvaluationContext scope(
- *this, ExpressionEvaluationContext::PotentiallyEvaluated);
- SourceLocation poi = var->getLocation();
- Expr *varRef =new (Context) DeclRefExpr(var, false, type, VK_LValue, poi);
- ExprResult result
- = PerformMoveOrCopyInitialization(
- InitializedEntity::InitializeBlock(poi, type, false),
- var, var->getType(), varRef, /*AllowNRVO=*/true);
- if (!result.isInvalid()) {
- result = MaybeCreateExprWithCleanups(result);
- Expr *init = result.getAs<Expr>();
- Context.setBlockVarCopyInit(var, init, canThrow(init));
- }
-
- // The destructor's exception spefication is needed when IRGen generates
- // block copy/destroy functions. Resolve it here.
- if (const CXXRecordDecl *RD = type->getAsCXXRecordDecl())
- if (CXXDestructorDecl *DD = RD->getDestructor()) {
- auto *FPT = DD->getType()->getAs<FunctionProtoType>();
- FPT = ResolveExceptionSpec(poi, FPT);
- }
- }
- }
+ if (var->hasAttr<BlocksAttr>())
+ getCurFunction()->addByrefBlockVar(var);
Expr *Init = var->getInit();
bool IsGlobal = GlobalStorage && !var->isStaticLocal();
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index fe48e47ff4..ba8ffbe2eb 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -13507,6 +13507,9 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc,
}
}
+ if (getCurFunction())
+ getCurFunction()->addBlock(BSI->TheDecl);
+
return Result;
}
diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp
index 00fad25eea..4825cb7ceb 100644
--- a/lib/Serialization/ASTReaderDecl.cpp
+++ b/lib/Serialization/ASTReaderDecl.cpp
@@ -1363,6 +1363,7 @@ ASTDeclReader::RedeclarableResult ASTDeclReader::VisitVarDeclImpl(VarDecl *VD) {
VD->NonParmVarDeclBits.IsInitCapture = Record.readInt();
VD->NonParmVarDeclBits.PreviousDeclInSameBlockScope = Record.readInt();
VD->NonParmVarDeclBits.ImplicitParamKind = Record.readInt();
+ VD->NonParmVarDeclBits.EscapingByref = Record.readInt();
}
auto VarLinkage = Linkage(Record.readInt());
VD->setCachedLinkage(VarLinkage);
diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp
index 8eb6c61525..97303da9a1 100644
--- a/lib/Serialization/ASTWriterDecl.cpp
+++ b/lib/Serialization/ASTWriterDecl.cpp
@@ -936,6 +936,7 @@ void ASTDeclWriter::VisitVarDecl(VarDecl *D) {
Record.push_back(static_cast<unsigned>(IPD->getParameterKind()));
else
Record.push_back(0);
+ Record.push_back(D->isEscapingByref());
}
Record.push_back(D->getLinkageInternal());
@@ -1006,6 +1007,7 @@ void ASTDeclWriter::VisitVarDecl(VarDecl *D) {
!D->isInitCapture() &&
!D->isPreviousDeclInSameBlockScope() &&
!(D->hasAttr<BlocksAttr>() && D->getType()->getAsCXXRecordDecl()) &&
+ !D->isEscapingByref() &&
D->getStorageDuration() != SD_Static &&
!D->getMemberSpecializationInfo())
AbbrevToUse = Writer.getDeclVarAbbrev();
@@ -2057,6 +2059,7 @@ void ASTWriter::WriteDeclAbbrevs() {
Abv->Add(BitCodeAbbrevOp(0)); // isInitCapture
Abv->Add(BitCodeAbbrevOp(0)); // isPrevDeclInSameScope
Abv->Add(BitCodeAbbrevOp(0)); // ImplicitParamKind
+ Abv->Add(BitCodeAbbrevOp(0)); // EscapingByref
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Linkage
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // IsInitICE (local)
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // VarKind (local enum)
diff --git a/test/CodeGen/block-byref-aggr.c b/test/CodeGen/block-byref-aggr.c
index 962f100638..db10576899 100644
--- a/test/CodeGen/block-byref-aggr.c
+++ b/test/CodeGen/block-byref-aggr.c
@@ -9,11 +9,13 @@ Agg makeAgg(void);
// cause a block copy. rdar://9309454
void test0() {
__block Agg a = {100};
+ ^{ (void)a; };
a = makeAgg();
}
// CHECK-LABEL: define void @test0()
// CHECK: [[A:%.*]] = alloca [[BYREF:%.*]], align 8
+// CHECK-NEXT: alloca <{ i8*, i32, i32, i8*, %{{.*}}*, i8* }>, align 8
// CHECK-NEXT: [[TEMP:%.*]] = alloca [[AGG]], align 4
// CHECK: [[RESULT:%.*]] = call i32 @makeAgg()
// CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[TEMP]], i32 0, i32 0
@@ -35,11 +37,13 @@ void test0() {
// rdar://11757470
void test1() {
__block Agg a, b;
+ ^{ (void)a; (void)b; };
a = b = makeAgg();
}
// CHECK-LABEL: define void @test1()
// CHECK: [[A:%.*]] = alloca [[A_BYREF:%.*]], align 8
// CHECK-NEXT: [[B:%.*]] = alloca [[B_BYREF:%.*]], align 8
+// CHECK-NEXT: alloca <{ i8*, i32, i32, i8*, %{{.*}}*, i8*, i8* }>, align 8
// CHECK-NEXT: [[TEMP:%.*]] = alloca [[AGG]], align 4
// CHECK: [[RESULT:%.*]] = call i32 @makeAgg()
// CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[TEMP]], i32 0, i32 0
diff --git a/test/CodeGen/blocks-seq.c b/test/CodeGen/blocks-seq.c
index b3e672976c..9b47fbf783 100644
--- a/test/CodeGen/blocks-seq.c
+++ b/test/CodeGen/blocks-seq.c
@@ -11,6 +11,7 @@ int rhs();
void foo() {
__block int i;
+ ^{ (void)i; };
i = rhs();
i += rhs();
}
diff --git a/test/CodeGen/exceptions.c b/test/CodeGen/exceptions.c
index 039ec84d2e..57b869196b 100644
--- a/test/CodeGen/exceptions.c
+++ b/test/CodeGen/exceptions.c
@@ -23,6 +23,7 @@ void test1() {
void test2_helper();
void test2() {
__block int x = 10;
+ ^{ (void)x; };
test2_helper(5, 6, 7);
}
void test2_helper(int x, int y) {
diff --git a/test/CodeGen/personality.c b/test/CodeGen/personality.c
index 1c533ce786..59a6a9ccd6 100644
--- a/test/CodeGen/personality.c
+++ b/test/CodeGen/personality.c
@@ -26,6 +26,7 @@ extern void i(void);
void f(void) {
__block int i;
+ ^{ (void)i; };
g(^ { });
}
diff --git a/test/CodeGenCXX/block-capture.cpp b/test/CodeGenCXX/block-capture.cpp
index 623838357a..515d64d35d 100644
--- a/test/CodeGenCXX/block-capture.cpp
+++ b/test/CodeGenCXX/block-capture.cpp
@@ -4,10 +4,12 @@
// CHECK: [[baz:%[0-9a-z_]*]] = alloca %struct.__block_byref_baz
// CHECK: [[bazref:%[0-9a-z_\.]*]] = getelementptr inbounds %struct.__block_byref_baz, %struct.__block_byref_baz* [[baz]], i32 0, i32 1
// CHECK: store %struct.__block_byref_baz* [[baz]], %struct.__block_byref_baz** [[bazref]]
+// CHECK: bitcast %struct.__block_byref_baz* [[baz]] to i8*
// CHECK: [[disposable:%[0-9a-z_]*]] = bitcast %struct.__block_byref_baz* [[baz]] to i8*
// CHECK: call void @_Block_object_dispose(i8* [[disposable]]
int main() {
__block int baz = [&]() { return 0; }();
+ ^{ (void)baz; };
return 0;
}
diff --git a/test/CodeGenCXX/blocks.cpp b/test/CodeGenCXX/blocks.cpp
index 32b1dd82dd..3b3363dc41 100644
--- a/test/CodeGenCXX/blocks.cpp
+++ b/test/CodeGenCXX/blocks.cpp
@@ -76,6 +76,7 @@ namespace test2 {
void test() {
__block A a;
__block B b;
+ ^{ (void)a; (void)b; };
}
// CHECK-LABEL: define internal void @__Block_byref_object_copy
diff --git a/test/CodeGenCXX/debug-info-blocks.cpp b/test/CodeGenCXX/debug-info-blocks.cpp
index fd2f0c99e7..f47ade7d52 100644
--- a/test/CodeGenCXX/debug-info-blocks.cpp
+++ b/test/CodeGenCXX/debug-info-blocks.cpp
@@ -9,6 +9,7 @@ struct A {
void test() {
__block A a;
+ ^{ (void)a; };
}
// CHECK: !DISubprogram(name: "__Block_byref_object_copy_",
diff --git a/test/CodeGenCXX/noescape.cpp b/test/CodeGenCXX/noescape.cpp
index 90d24de3da..40bc839788 100644
--- a/test/CodeGenCXX/noescape.cpp
+++ b/test/CodeGenCXX/noescape.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin -std=c++11 -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -std=c++11 -emit-llvm -fblocks -o - %s | FileCheck %s
struct S {
int a[4];
@@ -65,3 +65,32 @@ typedef void (*NoEscapeFunc)(__attribute__((noescape)) int *);
void test3(NoEscapeFunc f, int *p) {
f(p);
}
+
+namespace TestByref {
+
+struct S {
+ S();
+ ~S();
+ S(const S &);
+ int a;
+};
+
+typedef void (^BlockTy)(void);
+S &getS();
+void noescapefunc(__attribute__((noescape)) BlockTy);
+
+// Check that __block variables with reference types are handled correctly.
+
+// CHECK: define void @_ZN9TestByref4testEv(
+// CHECK: %[[X:.*]] = alloca %[[STRUCT_TESTBYREF:.*]]*, align 8
+// CHECK: %[[BLOCK:.*]] = alloca <{ i8*, i32, i32, i8*, %{{.*}}*, %[[STRUCT_TESTBYREF]]* }>, align 8
+// CHECK: %[[BLOCK_CAPTURED:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %{{.*}}*, %[[STRUCT_TESTBYREF]]* }>, <{ i8*, i32, i32, i8*, %{{.*}}*, %[[STRUCT_TESTBYREF]]* }>* %[[BLOCK]], i32 0, i32 5
+// CHECK: %[[V0:.*]] = load %[[STRUCT_TESTBYREF]]*, %[[STRUCT_TESTBYREF]]** %[[X]], align 8
+// CHECK: store %[[STRUCT_TESTBYREF]]* %[[V0]], %[[STRUCT_TESTBYREF]]** %[[BLOCK_CAPTURED]], align 8
+
+void test() {
+ __block S &x = getS();
+ noescapefunc(^{ (void)x; });
+}
+
+}
diff --git a/test/CodeGenObjC/arc-no-arc-exceptions.m b/test/CodeGenObjC/arc-no-arc-exceptions.m
index 3338ad8477..31d1dd5376 100644
--- a/test/CodeGenObjC/arc-no-arc-exceptions.m
+++ b/test/CodeGenObjC/arc-no-arc-exceptions.m
@@ -42,6 +42,7 @@ void NSLog(id, ...);
void test2(void) {
@autoreleasepool {
__attribute__((__blocks__(byref))) int x;
+ ^{ (void)x; };
NSLog(@"Address of x outside of block: %p", &x);
}
}
diff --git a/test/CodeGenObjC/arc-unoptimized-byref-var.m b/test/CodeGenObjC/arc-unoptimized-byref-var.m
index 9d856d659a..ffc73a44d4 100644
--- a/test/CodeGenObjC/arc-unoptimized-byref-var.m
+++ b/test/CodeGenObjC/arc-unoptimized-byref-var.m
@@ -3,6 +3,7 @@
void test19() {
__block id x;
+ ^{ (void)x; };
// CHECK-UNOPT-LABEL: define internal void @__Block_byref_object_copy
// CHECK-UNOPT: [[X:%.*]] = getelementptr inbounds [[BYREF_T:%.*]], [[BYREF_T:%.*]]* [[VAR:%.*]], i32 0, i32 6
// CHECK-UNOPT: [[X2:%.*]] = getelementptr inbounds [[BYREF_T:%.*]], [[BYREF_T:%.*]]* [[VAR1:%.*]], i32 0, i32 6
diff --git a/test/CodeGenObjC/blocks-1.m b/test/CodeGenObjC/blocks-1.m
index 0d2c35096a..cecb55d17a 100644
--- a/test/CodeGenObjC/blocks-1.m
+++ b/test/CodeGenObjC/blocks-1.m
@@ -1,23 +1,31 @@
-// RUN: %clang_cc1 %s -emit-llvm -o %t -fobjc-gc -fblocks -triple i386-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5
-// RUN: grep "_Block_object_dispose" %t | count 6
-// RUN: grep "__copy_helper_block_" %t | count 4
-// RUN: grep "__destroy_helper_block_" %t | count 4
-// RUN: grep "__Block_byref_object_copy_" %t | count 2
-// RUN: grep "__Block_byref_object_dispose_" %t | count 2
-// RUN: not grep "i32 135)" %t
-// RUN: grep "_Block_object_assign" %t | count 4
-// RUN: grep "objc_read_weak" %t | count 2
-// RUN: grep "objc_assign_weak" %t | count 3
-// RUN: %clang_cc1 -x objective-c++ %s -emit-llvm -o %t -fobjc-gc -fblocks -triple i386-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5
-// RUN: grep "_Block_object_dispose" %t | count 6
-// RUN: grep "__copy_helper_block_" %t | count 4
-// RUN: grep "__destroy_helper_block_" %t | count 4
-// RUN: grep "__Block_byref_object_copy_" %t | count 2
-// RUN: grep "__Block_byref_object_dispose_" %t | count 2
-// RUN: not grep "i32 135)" %t
-// RUN: grep "_Block_object_assign" %t | count 4
-// RUN: grep "objc_read_weak" %t | count 2
-// RUN: grep "objc_assign_weak" %t | count 3
+// RUN: %clang_cc1 %s -emit-llvm -o - -fobjc-gc -fblocks -triple i386-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 | FileCheck %s --check-prefix=CHECK --check-prefix=OBJC
+// RUN: %clang_cc1 -x objective-c++ %s -emit-llvm -o - -fobjc-gc -fblocks -triple i386-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 | FileCheck %s --check-prefix=CHECK --check-prefix=OBJCXX
+
+// OBJC-LABEL: define void @test1(
+// OBJCXX-LABEL: define void @_Z5test1P12NSDictionary(
+
+// CHECK-LABEL: define linkonce_odr hidden void @__copy_helper_block_
+// CHECK: call void @_Block_object_assign(
+
+// CHECK-LABEL: define linkonce_odr hidden void @__destroy_helper_block_
+// CHECK: call void @_Block_object_dispose(
+
+// OBJC-LABEL: define void @foo(
+// OBJCXX-LABEL: define void @_Z3foov(
+// CHECK: call i8* @objc_read_weak(
+// CHECK: call i8* @objc_assign_weak(
+// CHECK: call void @_Block_object_dispose(
+
+// OBJC-LABEL: define void @test2(
+// OBJCXX-LABEL: define void @_Z5test2v(
+// CHECK: call i8* @objc_assign_weak(
+// CHECK: call void @_Block_object_dispose(
+
+// CHECK-LABEL: define linkonce_odr hidden void @__copy_helper_block_
+// CHECK: call void @_Block_object_assign(
+
+// CHECK-LABEL: define linkonce_odr hidden void @__destroy_helper_block_
+// CHECK: call void @_Block_object_dispose(
@interface NSDictionary @end
@@ -30,6 +38,7 @@ void test1(NSDictionary * dict) {
void foo() {
__block __weak D *weakSelf;
+ ^{ (void)weakSelf; };
D *l;
l = weakSelf;
weakSelf = l;
diff --git a/test/CodeGenObjC/noescape.m b/test/CodeGenObjC/noescape.m
index b4c8bf7eaf..c7624f24fb 100644
--- a/test/CodeGenObjC/noescape.m
+++ b/test/CodeGenObjC/noescape.m
@@ -8,6 +8,7 @@ union U {
long long *ll;
} __attribute__((transparent_union));
+void escapingFunc0(BlockTy);
void noescapeFunc0(id, __attribute__((noescape)) BlockTy);
void noescapeFunc1(__attribute__((noescape)) int *);
void noescapeFunc2(__attribute__((noescape)) id);
@@ -21,6 +22,9 @@ void noescapeFunc3(__attribute__((noescape)) union U);
// When the block is non-escaping, copy/dispose helpers aren't generated, so the
// block layout string must include information about __strong captures.
+// CHECK-NOARC: %[[STRUCT_BLOCK_BYREF_B0:.*]] = type { i8*, %[[STRUCT_BLOCK_BYREF_B0]]*, i32, i32, i8*, %[[STRUCT_S0:.*]] }
+// CHECK-ARC: %[[STRUCT_BLOCK_BYREF_B0:.*]] = type { i8*, %[[STRUCT_BLOCK_BYREF_B0]]*, i32, i32, i8*, i8*, i8*, %[[STRUCT_S0:.*]] }
+// CHECK: %[[STRUCT_S0]] = type { i8*, i8* }
// CHECK: @[[BLOCK_DESCIPTOR_TMP_2:.*ls32l8"]] = linkonce_odr hidden unnamed_addr constant { i64, i64, i8*, i64 } { i64 0, i64 40, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i64 256 }, align 8
// CHECK-LABEL: define void @test0(
@@ -102,7 +106,7 @@ void test5(BlockTy2 b, int *p) {
// CHECK-NOARC: %[[V1:.*]] = load i8*, i8** %[[B_ADDR]], align 8
// CHECK-NOARC: store i8* %[[V1]], i8** %[[BLOCK_CAPTURED]], align 8
// CHECK-ARC: %[[V2:.*]] = load i8*, i8** %[[B_ADDR]], align 8
-// CHECK-ARC: %[[V3:.*]] = call i8* @objc_retain(i8* %[[V2]]) #3
+// CHECK-ARC: %[[V3:.*]] = call i8* @objc_retain(i8* %[[V2]])
// CHECK-ARC: store i8* %[[V3]], i8** %[[BLOCK_CAPTURED]], align 8
// CHECK: call void @noescapeFunc0(
// CHECK-ARC: call void @objc_storeStrong(i8** %[[V0]], i8* null)
@@ -118,3 +122,49 @@ void func(id);
void test6(id a, id b) {
noescapeFunc0(a, ^{ func(b); });
}
+
+// We don't need either the byref helper functions or the byref structs for
+// __block variables that are not captured by escaping blocks.
+
+// CHECK: define void @test7(
+// CHECK: alloca i8*, align 8
+// CHECK: %[[B0:.*]] = alloca i8*, align 8
+// CHECK: %[[BLOCK:.*]] = alloca <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8** }>, align 8
+// CHECK: %[[BLOCK_CAPTURED:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8** }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8** }>* %[[BLOCK]], i32 0, i32 5
+// CHECK: store i8** %[[B0]], i8*** %[[BLOCK_CAPTURED]], align 8
+
+// CHECK-ARC-NOT: define internal void @__Block_byref_object_copy_
+// CHECK-ARC-NOT: define internal void @__Block_byref_object_dispose_
+
+void test7() {
+ id a;
+ __block id b0;
+ noescapeFunc0(a, ^{ (void)b0; });
+}
+
+// __block variables captured by escaping blocks need byref helper functions.
+
+// CHECK: define void @test8(
+// CHECK: %[[A:.*]] = alloca i8*, align 8
+// CHECK: %[[B0:.*]] = alloca %[[STRUCT_BLOCK_BYREF_B0]], align 8
+// CHECK: alloca <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>, align 8
+// CHECK: %[[BLOCK1:.*]] = alloca <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>, align 8
+// CHECK: %[[BLOCK_CAPTURED7:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>* %[[BLOCK1]], i32 0, i32 5
+// CHECK: %[[V3:.*]] = bitcast %[[STRUCT_BLOCK_BYREF_B0]]* %[[B0]] to i8*
+// CHECK: store i8* %[[V3]], i8** %[[BLOCK_CAPTURED7]], align 8
+
+// CHECK-ARC: define internal void @__Block_byref_object_copy_
+// CHECK-ARC: define internal void @__Block_byref_object_dispose_
+// CHECK: define linkonce_odr hidden void @__copy_helper_block_
+// CHECK: define linkonce_odr hidden void @__destroy_helper_block_
+
+struct S0 {
+ id a, b;
+};
+
+void test8() {
+ id a;
+ __block struct S0 b0;
+ noescapeFunc0(a, ^{ (void)b0; });
+ escapingFunc0(^{ (void)b0; });
+}
diff --git a/test/CodeGenObjCXX/arc-blocks.mm b/test/CodeGenObjCXX/arc-blocks.mm
index ef0561bad5..4791aff0b3 100644
--- a/test/CodeGenObjCXX/arc-blocks.mm
+++ b/test/CodeGenObjCXX/arc-blocks.mm
@@ -3,9 +3,9 @@
// RUN: %clang_cc1 -std=gnu++98 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-runtime-has-weak -fblocks -fobjc-arc -o - %s | FileCheck -check-prefix CHECK-NOEXCP %s
// CHECK: [[A:.*]] = type { i64, [10 x i8*] }
+// CHECK: %[[STRUCT_BLOCK_DESCRIPTOR:.*]] = type { i64, i64 }
// CHECK: %[[STRUCT_TEST1_S0:.*]] = type { i32 }
// CHECK: %[[STRUCT_TRIVIAL_INTERNAL:.*]] = type { i32 }
-// CHECK: %[[STRUCT_BLOCK_DESCRIPTOR:.*]] = type { i64, i64 }
// CHECK: [[LAYOUT0:@.*]] = private unnamed_addr constant [3 x i8] c" 9\00"
@@ -20,6 +20,7 @@ namespace test0 {
void foo() {
__block A v;
+ ^{ (void)v; };
}
// CHECK-LABEL: define void @_ZN5test03fooEv()
// CHECK: [[V:%.*]] = alloca [[BYREF_A:%.*]], align 8
@@ -32,7 +33,8 @@ namespace test0 {
// CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[BYREF_A]], [[BYREF_A]]* [[V]], i32 0, i32 7
// CHECK-NEXT: call void @_ZN5test01AC1Ev([[A]]* [[T0]])
// CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[BYREF_A]], [[BYREF_A]]* [[V]], i32 0, i32 7
- // CHECK-NEXT: [[T1:%.*]] = bitcast [[BYREF_A]]* [[V]] to i8*
+ // CHECK: bitcast [[BYREF_A]]* [[V]] to i8*
+ // CHECK: [[T1:%.*]] = bitcast [[BYREF_A]]* [[V]] to i8*
// CHECK-NEXT: call void @_Block_object_dispose(i8* [[T1]], i32 8)
// CHECK-NEXT: call void @_ZN5test01AD1Ev([[A]]* [[T0]])
// CHECK-NEXT: ret void
@@ -53,6 +55,11 @@ namespace test0 {
// CHECK-NEXT: ret void
}
+// CHECK-LABEL: define linkonce_odr hidden void @__copy_helper_block_
+// CHECK-LABEL: define linkonce_odr hidden void @__destroy_helper_block_
+// CHECK-LABEL-O1: define linkonce_odr hidden void @__copy_helper_block_
+// CHECK-LABEL-O1: define linkonce_odr hidden void @__destroy_helper_block_
+
namespace test1 {
// Check that copy/dispose helper functions are exception safe.
diff --git a/test/PCH/block-helpers.cpp b/test/PCH/block-helpers.cpp
index 046bbb428c..02d4ceecf3 100644
--- a/test/PCH/block-helpers.cpp
+++ b/test/PCH/block-helpers.cpp
@@ -1,6 +1,26 @@
// RUN: %clang_cc1 -x c++-header -triple x86_64-apple-darwin11 -emit-pch -fblocks -fexceptions -o %t %S/block-helpers.h
// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -include-pch %t -emit-llvm -fblocks -fexceptions -o - %s | FileCheck %s
+// CHECK: %[[STRUCT_BLOCK_BYREF_X:.*]] = type { i8*, %[[STRUCT_BLOCK_BYREF_X]]*, i32, i32, i8*, i8*, %[[STRUCT_S0:.*]] }
+// CHECK: %[[STRUCT_S0]] = type { i32 }
+// CHECK: %[[STRUCT_BLOCK_BYREF_Y:.*]] = type { i8*, %[[STRUCT_BLOCK_BYREF_Y]]*, i32, i32, i8*, i8*, %[[STRUCT_S0]] }
+// CHECK: %[[STRUCT_BLOCK_DESCRIPTOR:.*]] = type { i64, i64 }
+
+// Check that byref structs are allocated for x and y.
+
+// CHECK-LABEL: define linkonce_odr void @_ZN1S1mEv(
+// CHECK: %[[X:.*]] = alloca %[[STRUCT_BLOCK_BYREF_X]], align 8
+// CHECK: %[[Y:.*]] = alloca %[[STRUCT_BLOCK_BYREF_Y]], align 8
+// CHECK: %[[BLOCK:.*]] = alloca <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8* }>, align 8
+// CHECK: %[[BLOCK_CAPTURED:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8* }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8* }>* %[[BLOCK]], i32 0, i32 5
+// CHECK: %[[V0:.*]] = bitcast %[[STRUCT_BLOCK_BYREF_X]]* %[[X]] to i8*
+// CHECK: store i8* %[[V0]], i8** %[[BLOCK_CAPTURED]], align 8
+// CHECK: %[[BLOCK_CAPTURED10:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8* }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8* }>* %[[BLOCK]], i32 0, i32 6
+// CHECK: %[[V1:.*]] = bitcast %[[STRUCT_BLOCK_BYREF_Y]]* %[[Y]] to i8*
+// CHECK: store i8* %[[V1]], i8** %[[BLOCK_CAPTURED10]], align 8
+
+// CHECK-LABEL: define internal void @___ZN1S1mEv_block_invoke(
+
// The second call to block_object_assign should be an invoke.
// CHECK-LABEL: define linkonce_odr hidden void @__copy_helper_block_e8_32rc40rc(
diff --git a/test/SemaObjCXX/blocks.mm b/test/SemaObjCXX/blocks.mm
index bdd5538e92..5f73524172 100644
--- a/test/SemaObjCXX/blocks.mm
+++ b/test/SemaObjCXX/blocks.mm
@@ -76,21 +76,27 @@ namespace N1 {
}
// Make sure we successfully instantiate the copy constructor of a
-// __block variable's type.
+// __block variable's type when the variable is captured by an escaping block.
namespace N2 {
template <int n> struct A {
A() {}
A(const A &other) {
int invalid[-n]; // expected-error 2 {{array with a negative size}}
}
+ void m() {}
};
+ typedef void (^BlockFnTy)();
+ void func(BlockFnTy);
+
void test1() {
__block A<1> x; // expected-note {{requested here}}
+ func(^{ x.m(); });
}
template <int n> void test2() {
__block A<n> x; // expected-note {{requested here}}
+ func(^{ x.m(); });
}
template void test2<2>();
}
diff --git a/test/SemaObjCXX/noescape.mm b/test/SemaObjCXX/noescape.mm
index 9432a3a48a..efa2a76d66 100644
--- a/test/SemaObjCXX/noescape.mm
+++ b/test/SemaObjCXX/noescape.mm
@@ -8,6 +8,7 @@ struct S {
void m();
};
+void escapingFunc0(BlockTy);
void noescapeFunc0(id, __attribute__((noescape)) BlockTy);
void noescapeFunc1(id, [[clang::noescape]] BlockTy);
void noescapeFunc2(__attribute__((noescape)) int *); // expected-note {{previous declaration is here}}
@@ -127,3 +128,27 @@ __attribute__((objc_root_class))
-(void) m1:(int*) p {
}
@end
+
+struct S6 {
+ S6();
+ S6(const S6 &) = delete; // expected-note 3 {{'S6' has been explicitly marked deleted here}}
+ int f;
+};
+
+void test1() {
+ id a;
+ // __block variables that are not captured by escaping blocks don't
+ // necessitate having accessible copy constructors.
+ __block S6 b0;
+ __block S6 b1; // expected-error {{call to deleted constructor of 'S6'}}
+ __block S6 b2; // expected-error {{call to deleted constructor of 'S6'}}
+ __block S6 b3; // expected-error {{call to deleted constructor of 'S6'}}
+
+ noescapeFunc0(a, ^{ (void)b0; });
+ escapingFunc0(^{ (void)b1; });
+ {
+ noescapeFunc0(a, ^{ (void)b0; (void)b1; });
+ }
+ noescapeFunc0(a, ^{ escapingFunc0(^{ (void)b2; }); });
+ escapingFunc0(^{ noescapeFunc0(a, ^{ (void)b3; }); });
+}