GlobalDecl(const VarDecl *D) { Init(D);}
GlobalDecl(const FunctionDecl *D) { Init(D); }
GlobalDecl(const BlockDecl *D) { Init(D); }
+ GlobalDecl(const CapturedDecl *D) { Init(D); }
GlobalDecl(const ObjCMethodDecl *D) { Init(D); }
GlobalDecl(const CXXConstructorDecl *D, CXXCtorType Type)
"duration">;
def err_this_capture : Error<
"'this' cannot be %select{implicitly |}0captured in this context">;
- def err_lambda_capture_block : Error<
- "__block variable %0 cannot be captured in a lambda expression">;
def err_lambda_capture_anonymous_var : Error<
"unnamed variable cannot be implicitly captured in a lambda expression">;
def err_lambda_capture_vm_type : Error<
def err_return_in_captured_stmt : Error<
"cannot return from %0">;
+def err_capture_block_variable : Error<
+ "__block variable %0 cannot be captured in a "
+ "%select{lambda expression|captured statement}1">;
def err_operator_arrow_circular : Error<
"circular pointer delegation detected">;
NameStream.flush();
Out << Name.size() << Name;
return;
+ } else if (isa<CapturedDecl>(DC)) {
+ // Skip CapturedDecl context.
+ manglePrefix(getEffectiveParentContext(DC), NoFunction);
+ return;
}
const NamedDecl *ND = cast<NamedDecl>(DC);
Context.mangleBlock(BD, Out);
Out << '@';
return manglePostfix(DC->getParent(), NoFunction);
+ } else if (isa<CapturedDecl>(DC)) {
+ // Skip CapturedDecl context.
+ manglePostfix(DC->getParent(), NoFunction);
+ return;
}
if (NoFunction && (isa<FunctionDecl>(DC) || isa<ObjCMethodDecl>(DC)))
return CGF.MakeAddrLValue(V, E->getType(), Alignment);
}
+static LValue EmitCapturedFieldLValue(CodeGenFunction &CGF, const FieldDecl *FD,
+ llvm::Value *ThisValue) {
+ QualType TagType = CGF.getContext().getTagDeclType(FD->getParent());
+ LValue LV = CGF.MakeNaturalAlignAddrLValue(ThisValue, TagType);
+ return CGF.EmitLValueForField(LV, FD);
+}
+
LValue CodeGenFunction::EmitDeclRefLValue(const DeclRefExpr *E) {
const NamedDecl *ND = E->getDecl();
CharUnits Alignment = getContext().getDeclAlign(ND);
// Use special handling for lambdas.
if (!V) {
if (FieldDecl *FD = LambdaCaptureFields.lookup(VD)) {
- QualType LambdaTagType = getContext().getTagDeclType(FD->getParent());
- LValue LambdaLV = MakeNaturalAlignAddrLValue(CXXABIThisValue,
- LambdaTagType);
- return EmitLValueForField(LambdaLV, FD);
+ return EmitCapturedFieldLValue(*this, FD, CXXABIThisValue);
+ } else if (CapturedStmtInfo) {
+ if (const FieldDecl *FD = CapturedStmtInfo->lookup(VD))
+ return EmitCapturedFieldLValue(*this, FD,
+ CapturedStmtInfo->getContextValue());
}
assert(isa<BlockDecl>(CurCodeDecl) && E->refersToEnclosingLocal());
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/InlineAsm.h"
#include "llvm/IR/Intrinsics.h"
+#include "llvm/Support/CallSite.h"
using namespace clang;
using namespace CodeGen;
case Stmt::GCCAsmStmtClass: // Intentional fall-through.
case Stmt::MSAsmStmtClass: EmitAsmStmt(cast<AsmStmt>(*S)); break;
case Stmt::CapturedStmtClass:
- EmitCapturedStmt(cast<CapturedStmt>(*S));
+ EmitCapturedStmt(cast<CapturedStmt>(*S), CR_Default);
break;
case Stmt::ObjCAtTryStmtClass:
EmitObjCAtTryStmt(cast<ObjCAtTryStmt>(*S));
}
}
-void CodeGenFunction::EmitCapturedStmt(const CapturedStmt &S) {
- llvm_unreachable("not implemented yet");
+static LValue InitCapturedStruct(CodeGenFunction &CGF, const CapturedStmt &S) {
+ const RecordDecl *RD = S.getCapturedRecordDecl();
+ QualType RecordTy = CGF.getContext().getRecordType(RD);
+
+ // Initialize the captured struct.
+ LValue SlotLV = CGF.MakeNaturalAlignAddrLValue(
+ CGF.CreateMemTemp(RecordTy, "agg.captured"), RecordTy);
+
+ RecordDecl::field_iterator CurField = RD->field_begin();
+ for (CapturedStmt::capture_init_iterator I = S.capture_init_begin(),
+ E = S.capture_init_end();
+ I != E; ++I, ++CurField) {
+ LValue LV = CGF.EmitLValueForFieldInitialization(SlotLV, *CurField);
+ CGF.EmitInitializerForField(*CurField, LV, *I, ArrayRef<VarDecl *>());
+ }
+
+ return SlotLV;
+}
+
+/// Generate an outlined function for the body of a CapturedStmt, store any
+/// captured variables into the captured struct, and call the outlined function.
+llvm::Function *
+CodeGenFunction::EmitCapturedStmt(const CapturedStmt &S, CapturedRegionKind K) {
+ const CapturedDecl *CD = S.getCapturedDecl();
+ const RecordDecl *RD = S.getCapturedRecordDecl();
+ assert(CD->hasBody() && "missing CapturedDecl body");
+
+ LValue CapStruct = InitCapturedStruct(*this, S);
+
+ // Emit the CapturedDecl
+ CodeGenFunction CGF(CGM, true);
+ CGF.CapturedStmtInfo = new CGCapturedStmtInfo(S, K);
+ llvm::Function *F = CGF.GenerateCapturedStmtFunction(CD, RD);
+ delete CGF.CapturedStmtInfo;
+
+ // Emit call to the helper function.
+ EmitCallOrInvoke(F, CapStruct.getAddress());
+
+ return F;
+}
+
+/// Creates the outlined function for a CapturedStmt.
+llvm::Function *
+CodeGenFunction::GenerateCapturedStmtFunction(const CapturedDecl *CD,
+ const RecordDecl *RD) {
+ assert(CapturedStmtInfo &&
+ "CapturedStmtInfo should be set when generating the captured function");
+
+ // Check if we should generate debug info for this function.
+ maybeInitializeDebugInfo();
+
+ // Build the argument list.
+ ASTContext &Ctx = CGM.getContext();
+ FunctionArgList Args;
+ Args.append(CD->param_begin(), CD->param_end());
+
+ // Create the function declaration.
+ FunctionType::ExtInfo ExtInfo;
+ const CGFunctionInfo &FuncInfo =
+ CGM.getTypes().arrangeFunctionDeclaration(Ctx.VoidTy, Args, ExtInfo,
+ /*IsVariadic=*/false);
+ llvm::FunctionType *FuncLLVMTy = CGM.getTypes().GetFunctionType(FuncInfo);
+
+ llvm::Function *F =
+ llvm::Function::Create(FuncLLVMTy, llvm::GlobalValue::InternalLinkage,
+ CapturedStmtInfo->getHelperName(), &CGM.getModule());
+ CGM.SetInternalFunctionAttributes(CD, F, FuncInfo);
+
+ // Generate the function.
+ StartFunction(CD, Ctx.VoidTy, F, FuncInfo, Args, CD->getBody()->getLocStart());
+
+ // Set the context parameter in CapturedStmtInfo.
+ llvm::Value *DeclPtr = LocalDeclMap[CD->getContextParam()];
+ assert(DeclPtr && "missing context parameter for CapturedStmt");
+ CapturedStmtInfo->setContextValue(Builder.CreateLoad(DeclPtr));
+
+ // If 'this' is captured, load it into CXXThisValue.
+ if (CapturedStmtInfo->isCXXThisExprCaptured()) {
+ FieldDecl *FD = CapturedStmtInfo->getThisFieldDecl();
+ LValue LV = MakeNaturalAlignAddrLValue(CapturedStmtInfo->getContextValue(),
+ Ctx.getTagDeclType(RD));
+ LValue ThisLValue = EmitLValueForField(LV, FD);
+
+ CXXThisValue = EmitLoadOfLValue(ThisLValue).getScalarVal();
+ }
+
+ CapturedStmtInfo->EmitBody(*this, CD->getBody());
+ FinishFunction(CD->getBodyRBrace());
+
+ return F;
}
CodeGenFunction::CodeGenFunction(CodeGenModule &cgm, bool suppressNewContext)
: CodeGenTypeCache(cgm), CGM(cgm), Target(cgm.getTarget()),
Builder(cgm.getModule().getContext()),
+ CapturedStmtInfo(0),
SanitizePerformTypeCheck(CGM.getSanOpts().Null |
CGM.getSanOpts().Alignment |
CGM.getSanOpts().ObjectSize |
return V;
}
+
+CodeGenFunction::CGCapturedStmtInfo::~CGCapturedStmtInfo() { }
#include "clang/AST/ExprObjC.h"
#include "clang/AST/Type.h"
#include "clang/Basic/ABI.h"
+#include "clang/Basic/CapturedStmt.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Frontend/CodeGenOptions.h"
#include "llvm/ADT/ArrayRef.h"
/// we prefer to insert allocas.
llvm::AssertingVH<llvm::Instruction> AllocaInsertPt;
+ /// \brief API for captured statement code generation.
+ class CGCapturedStmtInfo {
+ public:
+ explicit CGCapturedStmtInfo(const CapturedStmt &S,
+ CapturedRegionKind K = CR_Default)
+ : Kind(K), ThisValue(0), CXXThisFieldDecl(0) {
+
+ RecordDecl::field_iterator Field =
+ S.getCapturedRecordDecl()->field_begin();
+ for (CapturedStmt::const_capture_iterator I = S.capture_begin(),
+ E = S.capture_end();
+ I != E; ++I, ++Field) {
+ if (I->capturesThis())
+ CXXThisFieldDecl = *Field;
+ else
+ CaptureFields[I->getCapturedVar()] = *Field;
+ }
+ }
+
+ virtual ~CGCapturedStmtInfo();
+
+ CapturedRegionKind getKind() const { return Kind; }
+
+ void setContextValue(llvm::Value *V) { ThisValue = V; }
+ // \brief Retrieve the value of the context parameter.
+ llvm::Value *getContextValue() const { return ThisValue; }
+
+ /// \brief Lookup the captured field decl for a variable.
+ const FieldDecl *lookup(const VarDecl *VD) const {
+ return CaptureFields.lookup(VD);
+ }
+
+ bool isCXXThisExprCaptured() const { return CXXThisFieldDecl != 0; }
+ FieldDecl *getThisFieldDecl() const { return CXXThisFieldDecl; }
+
+ /// \brief Emit the captured statement body.
+ virtual void EmitBody(CodeGenFunction &CGF, Stmt *S) {
+ CGF.EmitStmt(S);
+ }
+
+ /// \brief Get the name of the capture helper.
+ virtual StringRef getHelperName() const { return "__captured_stmt"; }
+
+ private:
+ /// \brief The kind of captured statement being generated.
+ CapturedRegionKind Kind;
+
+ /// \brief Keep the map between VarDecl and FieldDecl.
+ llvm::SmallDenseMap<const VarDecl *, FieldDecl *> CaptureFields;
+
+ /// \brief The base address of the captured record, passed in as the first
+ /// argument of the parallel region function.
+ llvm::Value *ThisValue;
+
+ /// \brief Captured 'this' type.
+ FieldDecl *CXXThisFieldDecl;
+ };
+ CGCapturedStmtInfo *CapturedStmtInfo;
+
/// BoundsChecking - Emit run-time bounds checks. Higher values mean
/// potentially higher performance penalties.
unsigned char BoundsChecking;
void EmitCaseStmt(const CaseStmt &S);
void EmitCaseStmtRange(const CaseStmt &S);
void EmitAsmStmt(const AsmStmt &S);
- void EmitCapturedStmt(const CapturedStmt &S);
void EmitObjCForCollectionStmt(const ObjCForCollectionStmt &S);
void EmitObjCAtTryStmt(const ObjCAtTryStmt &S);
void EmitCXXTryStmt(const CXXTryStmt &S);
void EmitCXXForRangeStmt(const CXXForRangeStmt &S);
+ llvm::Function *EmitCapturedStmt(const CapturedStmt &S, CapturedRegionKind K);
+ llvm::Function *GenerateCapturedStmtFunction(const CapturedDecl *CD,
+ const RecordDecl *RD);
+
//===--------------------------------------------------------------------===//
// LValue Expression Emission
//===--------------------------------------------------------------------===//
return true;
}
}
- // Lambdas are not allowed to capture __block variables; they don't
- // support the expected semantics.
- if (IsLambda && HasBlocksAttr) {
+ // Lambdas and captured statements are not allowed to capture __block
+ // variables; they don't support the expected semantics.
+ if (HasBlocksAttr && (IsLambda || isa<CapturedRegionScopeInfo>(CSI))) {
if (BuildAndDiagnose) {
- Diag(Loc, diag::err_lambda_capture_block)
- << Var->getDeclName();
+ Diag(Loc, diag::err_capture_block_variable)
+ << Var->getDeclName() << !IsLambda;
Diag(Var->getLocation(), diag::note_previous_decl)
<< Var->getDeclName();
}
--- /dev/null
+// RUN: %clang_cc1 -fblocks -emit-llvm %s -o %t
+// RUN: FileCheck %s -input-file=%t -check-prefix=CHECK1
+// RUN: FileCheck %s -input-file=%t -check-prefix=CHECK2
+
+struct A {
+ int a;
+ float b;
+ char c;
+};
+
+void test_nest_captured_stmt(int param) {
+ int w;
+ // CHECK1: %struct.anon{{.*}} = type { i32*, i32* }
+ // CHECK1: %struct.anon{{.*}} = type { i32*, i32*, i32**, i32* }
+ // CHECK1: [[T:%struct.anon.*]] = type { i32*, i32*, %struct.A*, i32**, i32* }
+ #pragma clang __debug captured
+ {
+ int x;
+ int *y = &w;
+ #pragma clang __debug captured
+ {
+ struct A z;
+ #pragma clang __debug captured
+ {
+ w = x = z.a = 1;
+ *y = param;
+ z.b = 0.1f;
+ z.c = 'c';
+
+ // CHECK1: define internal void @__captured_stmt{{.*}}([[T]]
+ //
+ // CHECK1: getelementptr inbounds [[T]]* {{.*}}, i32 0, i32 2
+ // CHECK1-NEXT: load %struct.A**
+ // CHECK1-NEXT: getelementptr inbounds %struct.A*
+ // CHECK1-NEXT: store i32 1
+ //
+ // CHECK1: getelementptr inbounds [[T]]* {{.*}}, i32 0, i32 1
+ // CHECK1-NEXT: load i32**
+ // CHECK1-NEXT: store i32 1
+ //
+ // CHECK1: getelementptr inbounds [[T]]* {{.*}}, i32 0, i32 0
+ // CHECK1-NEXT: load i32**
+ // CHECK1-NEXT: store i32 1
+ //
+ // CHECK1: getelementptr inbounds [[T]]* {{.*}}, i32 0, i32 4
+ // CHECK1-NEXT: load i32**
+ // CHECK1-NEXT: load i32*
+ // CHECK1-NEXT: getelementptr inbounds [[T]]* {{.*}}, i32 0, i32 3
+ // CHECK1-NEXT: load i32***
+ // CHECK1-NEXT: load i32**
+ // CHECK1-NEXT: store i32
+ //
+ // CHECK1: getelementptr inbounds [[T]]* {{.*}}, i32 0, i32 2
+ // CHECK1-NEXT: load %struct.A**
+ // CHECK1-NEXT: getelementptr inbounds %struct.A*
+ // CHECK1-NEXT: store float
+ //
+ // CHECK1: getelementptr inbounds [[T]]* {{.*}}, i32 0, i32 2
+ // CHECK1-NEXT: load %struct.A**
+ // CHECK1-NEXT: getelementptr inbounds %struct.A*
+ // CHECK1-NEXT: store i8 99
+ }
+ }
+ }
+}
+
+void test_nest_block() {
+ __block int x;
+ int y;
+ ^{
+ int z;
+ x = z;
+ #pragma clang __debug captured
+ {
+ z = y; // OK
+ }
+ }();
+
+ // CHECK2: define internal void @{{.*}}test_nest_block_block_invoke
+ //
+ // CHECK2: [[Z:%[0-9a-z_]*]] = alloca i32
+ // CHECK2: alloca %struct.anon{{.*}}
+ //
+ // CHECK2: store i32
+ // CHECK2: store i32* [[Z]]
+ //
+ // CHECK2: getelementptr inbounds %struct.anon
+ // CHECK2-NEXT: getelementptr inbounds
+ // CHECK2-NEXT: store i32*
+ //
+ // CHECK2: call void @__captured_stmt
+
+ int a;
+ #pragma clang __debug captured
+ {
+ __block int b;
+ int c;
+ __block int d;
+ ^{
+ b = a;
+ b = c;
+ b = d;
+ }();
+ }
+
+ // CHECK2: alloca %struct.__block_byref_b
+ // CHECK2-NEXT: [[C:%[0-9a-z_]*]] = alloca i32
+ // CHECK2-NEXT: alloca %struct.__block_byref_d
+ //
+ // CHECK2: bitcast %struct.__block_byref_b*
+ // CHECK2-NEXT: store i8*
+ //
+ // CHECK2: [[CapA:%[0-9a-z_.]*]] = getelementptr inbounds {{.*}}, i32 0, i32 7
+ //
+ // CHECK2: getelementptr inbounds %struct.anon{{.*}}, i32 0, i32 0
+ // CHECK2: load i32**
+ // CHECK2: load i32*
+ // CHECK2: store i32 {{.*}}, i32* [[CapA]]
+ //
+ // CHECK2: [[CapC:%[0-9a-z_.]*]] = getelementptr inbounds {{.*}}, i32 0, i32 8
+ // CHECK2-NEXT: [[Val:%[0-9a-z_]*]] = load i32* [[C]]
+ // CHECK2-NEXT: store i32 [[Val]], i32* [[CapC]]
+ //
+ // CHECK2: bitcast %struct.__block_byref_d*
+ // CHECK2-NEXT: store i8*
+}
--- /dev/null
+// RUN: %clang_cc1 -emit-llvm %s -o %t
+// RUN: FileCheck %s -input-file=%t -check-prefix=CHECK-GLOBALS
+// RUN: FileCheck %s -input-file=%t -check-prefix=CHECK-1
+// RUN: FileCheck %s -input-file=%t -check-prefix=CHECK-2
+// RUN: FileCheck %s -input-file=%t -check-prefix=CHECK-3
+
+int foo();
+int global;
+
+// Single statement
+void test1() {
+ int i = 0;
+ #pragma clang __debug captured
+ {
+ i++;
+ }
+ // CHECK-1: %struct.anon = type { i32* }
+ //
+ // CHECK-1: test1
+ // CHECK-1: alloca %struct.anon
+ // CHECK-1: getelementptr inbounds %struct.anon*
+ // CHECK-1: store i32* %i
+ // CHECK-1: call void @[[HelperName:__captured_stmt[0-9]+]]
+}
+
+// CHECK-1: define internal void @[[HelperName]](%struct.anon
+// CHECK-1: getelementptr inbounds %struct.anon{{.*}}, i32 0, i32 0
+// CHECK-1: load i32**
+// CHECK-1: load i32*
+// CHECK-1: add nsw i32
+// CHECK-1: store i32
+
+// Compound statement with local variable
+void test2(int x) {
+ #pragma clang __debug captured
+ {
+ int i;
+ for (i = 0; i < x; i++)
+ foo();
+ }
+ // CHECK-2: test2
+ // CHECK-2-NOT: %i
+ // CHECK-2: call void @[[HelperName:__captured_stmt[0-9]+]]
+}
+
+// CHECK-2: define internal void @[[HelperName]]
+// CHECK-2-NOT: }
+// CHECK-2: %i = alloca i32
+
+// Capture array
+void test3() {
+ int arr[] = {1, 2, 3, 4, 5};
+ #pragma clang __debug captured
+ {
+ arr[2] = arr[1];
+ }
+ // CHECK-3: test3
+ // CHECK-3: alloca [5 x i32]
+ // CHECK-3: call void @__captured_stmt
+}
+
+void dont_capture_global() {
+ static int s;
+ extern int e;
+ #pragma clang __debug captured
+ {
+ global++;
+ s++;
+ e++;
+ }
+
+ // CHECK-GLOBALS: %[[Capture:struct\.anon[\.0-9]*]] = type {}
+ // CHECK-GLOBALS: call void @__captured_stmt[[HelperName:[0-9]+]](%[[Capture]]
+}
+
+// CHECK-GLOBALS: define internal void @__captured_stmt[[HelperName]]
+// CHECK-GLOBALS-NOT: ret
+// CHECK-GLOBALS: load i32* @global
+// CHECK-GLOBALS: load i32* @
+// CHECK-GLOBALS: load i32* @e
--- /dev/null
+// RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o %t
+// RUN: FileCheck %s -input-file=%t -check-prefix=CHECK-1
+// RUN: FileCheck %s -input-file=%t -check-prefix=CHECK-2
+// RUN: FileCheck %s -input-file=%t -check-prefix=CHECK-3
+// RUN: FileCheck %s -input-file=%t -check-prefix=CHECK-4
+
+struct Foo {
+ int x;
+ float y;
+ ~Foo() {}
+};
+
+struct TestClass {
+ int x;
+
+ TestClass() : x(0) {};
+ void MemberFunc() {
+ Foo f;
+ #pragma clang __debug captured
+ {
+ f.y = x;
+ }
+ }
+};
+
+void test1() {
+ TestClass c;
+ c.MemberFunc();
+ // CHECK-1: %[[Capture:struct\.anon[\.0-9]*]] = type { %struct.Foo*, %struct.TestClass* }
+
+ // CHECK-1: define {{.*}} void @_ZN9TestClass10MemberFuncEv
+ // CHECK-1: alloca %struct.anon
+ // CHECK-1: getelementptr inbounds %[[Capture]]* %{{[^,]*}}, i32 0, i32 0
+ // CHECK-1: store %struct.Foo* %f, %struct.Foo**
+ // CHECK-1: getelementptr inbounds %[[Capture]]* %{{[^,]*}}, i32 0, i32 1
+ // CHECK-1: call void @[[HelperName:[A-Za-z0-9_]+]](%[[Capture]]*
+ // CHECK-1: call void @_ZN3FooD1Ev
+ // CHECK-1: ret
+}
+
+// CHECK-1: define internal void @[[HelperName]]
+// CHECK-1: getelementptr inbounds %[[Capture]]* {{[^,]*}}, i32 0, i32 1
+// CHECK-1: getelementptr inbounds %struct.TestClass* {{[^,]*}}, i32 0, i32 0
+// CHECK-1: getelementptr inbounds %[[Capture]]* {{[^,]*}}, i32 0, i32 0
+
+void test2(int x) {
+ int y = [&]() {
+ #pragma clang __debug captured
+ {
+ x++;
+ }
+ return x;
+ }();
+
+ // CHECK-2: define void @_Z5test2i
+ // CHECK-2: call i32 @[[Lambda:["$\w]+]]
+ //
+ // CHECK-2: define internal i32 @[[Lambda]]
+ // CHECK-2: call void @[[HelperName:["$_A-Za-z0-9]+]](%[[Capture:.*]]*
+ //
+ // CHECK-2: define internal void @[[HelperName]]
+ // CHECK-2: getelementptr inbounds %[[Capture]]*
+ // CHECK-2: load i32**
+ // CHECK-2: load i32*
+}
+
+void test3(int x) {
+ #pragma clang __debug captured
+ {
+ x = [=]() { return x + 1; } ();
+ }
+
+ // CHECK-3: %[[Capture:struct\.anon[\.0-9]*]] = type { i32* }
+
+ // CHECK-3: define void @_Z5test3i(i32 %x)
+ // CHECK-3: store i32*
+ // CHECK-3: call void @{{.*}}__captured_stmt
+ // CHECK-3: ret void
+}
+
+void test4() {
+ #pragma clang __debug captured
+ {
+ Foo f;
+ f.x = 5;
+ }
+ // CHECK-4: %[[Capture:struct\.anon[\.0-9]*]] = type { i32* }
+
+ // CHECK-4: define void @_Z5test3i(i32 %x)
+ // CHECK-4: store i32*
+ // CHECK-4: call void @[[HelperName:["$_A-Za-z0-9]+]](%[[Capture:.*]]*
+ // CHECK-4: ret void
+ //
+ // CHECK-4: define internal void @[[HelperName]]
+ // CHECK-4: store i32 5, i32*
+ // CHECK-4: call void @{{.*}}FooD1Ev(%struct.Foo*
+}
}
void test_nest_block() {
- __block int x;
+ __block int x; // expected-note {{'x' declared here}}
int y;
^{
int z;
#pragma clang __debug captured
{
- x = y; // OK
+ x = y; // expected-error{{__block variable 'x' cannot be captured in a captured statement}}
y = z; // expected-error{{variable is not assignable (missing __block type specifier)}}
z = y; // OK
}
}();
- __block int a;
+ __block int a; // expected-note 2 {{'a' declared here}}
int b;
#pragma clang __debug captured
{
__block int c;
int d;
^{
- a = b; // OK
- a = c; // OK
+ a = b; // expected-error{{__block variable 'a' cannot be captured in a captured statement}}
b = d; // OK - Consistent with block inside a lambda
- c = a; // OK
+ c = a; // expected-error{{__block variable 'a' cannot be captured in a captured statement}}
+ c = d; // OK
d = b; // expected-error{{variable is not assignable (missing __block type specifier)}}
}();
}