r181000 - Correctly emit certain implicit references to 'self' even within

John McCall rjmccall at apple.com
Fri May 3 00:33:42 PDT 2013


Author: rjmccall
Date: Fri May  3 02:33:41 2013
New Revision: 181000

URL: http://llvm.org/viewvc/llvm-project?rev=181000&view=rev
Log:
Correctly emit certain implicit references to 'self' even within
a lambda.

Bug #1 is that CGF's CurFuncDecl was "stuck" at lambda invocation
functions.  Fix that by generally improving getNonClosureContext
to look through lambdas and captured statements but only report
code contexts, which is generally what's wanted.  Audit uses of
CurFuncDecl and getNonClosureAncestor for correctness.

Bug #2 is that lambdas weren't specially mapping 'self' when inside
an ObjC method.  Fix that by removing the requirement for that
and using the normal EmitDeclRefLValue path in LoadObjCSelf.

rdar://13800041

Modified:
    cfe/trunk/include/clang/AST/DeclBase.h
    cfe/trunk/lib/AST/DeclBase.cpp
    cfe/trunk/lib/CodeGen/CGBlocks.cpp
    cfe/trunk/lib/CodeGen/CGCall.cpp
    cfe/trunk/lib/CodeGen/CGClass.cpp
    cfe/trunk/lib/CodeGen/CGExpr.cpp
    cfe/trunk/lib/CodeGen/CGObjC.cpp
    cfe/trunk/lib/CodeGen/CodeGenFunction.cpp
    cfe/trunk/lib/CodeGen/CodeGenFunction.h
    cfe/trunk/lib/Sema/SemaExprObjC.cpp
    cfe/trunk/test/CodeGenObjC/debug-info-block-captured-self.m
    cfe/trunk/test/CodeGenObjC/debug-info-blocks.m
    cfe/trunk/test/CodeGenObjCXX/lambda-expressions.mm

Modified: cfe/trunk/include/clang/AST/DeclBase.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclBase.h?rev=181000&r1=180999&r2=181000&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/DeclBase.h (original)
+++ cfe/trunk/include/clang/AST/DeclBase.h Fri May  3 02:33:41 2013
@@ -372,10 +372,13 @@ public:
     return const_cast<Decl*>(this)->getDeclContext();
   }
 
-  /// Finds the innermost non-closure context of this declaration.
-  /// That is, walk out the DeclContext chain, skipping any blocks.
-  DeclContext *getNonClosureContext();
-  const DeclContext *getNonClosureContext() const {
+  /// Find the innermost non-closure ancestor of this declaration,
+  /// walking up through blocks, lambdas, etc.  If that ancestor is
+  /// not a code context (!isFunctionOrMethod()), returns null.
+  ///
+  /// A declaration may be its own non-closure context.
+  Decl *getNonClosureContext();
+  const Decl *getNonClosureContext() const {
     return const_cast<Decl*>(this)->getNonClosureContext();
   }
 
@@ -1114,8 +1117,8 @@ public:
   /// \brief Find the nearest non-closure ancestor of this context,
   /// i.e. the innermost semantic parent of this context which is not
   /// a closure.  A context may be its own non-closure ancestor.
-  DeclContext *getNonClosureAncestor();
-  const DeclContext *getNonClosureAncestor() const {
+  Decl *getNonClosureAncestor();
+  const Decl *getNonClosureAncestor() const {
     return const_cast<DeclContext*>(this)->getNonClosureAncestor();
   }
 

Modified: cfe/trunk/lib/AST/DeclBase.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclBase.cpp?rev=181000&r1=180999&r2=181000&view=diff
==============================================================================
--- cfe/trunk/lib/AST/DeclBase.cpp (original)
+++ cfe/trunk/lib/AST/DeclBase.cpp Fri May  3 02:33:41 2013
@@ -704,21 +704,37 @@ void Decl::CheckAccessDeclContext() cons
 #endif
 }
 
-DeclContext *Decl::getNonClosureContext() {
-  return getDeclContext()->getNonClosureAncestor();
-}
+static Decl::Kind getKind(const Decl *D) { return D->getKind(); }
+static Decl::Kind getKind(const DeclContext *DC) { return DC->getDeclKind(); }
 
-DeclContext *DeclContext::getNonClosureAncestor() {
-  DeclContext *DC = this;
+/// Starting at a given context (a Decl or DeclContext), look for a
+/// code context that is not a closure (a lambda, block, etc.).
+template <class T> static Decl *getNonClosureContext(T *D) {
+  if (getKind(D) == Decl::CXXMethod) {
+    CXXMethodDecl *MD = cast<CXXMethodDecl>(D);
+    if (MD->getParent()->isLambda() &&
+        MD->getOverloadedOperator() == OO_Call)
+      return getNonClosureContext(MD->getParent()->getParent());
+    return MD;
+  } else if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
+    return FD;
+  } else if (ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
+    return MD;
+  } else if (BlockDecl *BD = dyn_cast<BlockDecl>(D)) {
+    return getNonClosureContext(BD->getParent());
+  } else if (CapturedDecl *CD = dyn_cast<CapturedDecl>(D)) {
+    return getNonClosureContext(CD->getParent());
+  } else {
+    return 0;
+  }
+}
 
-  // This is basically "while (DC->isClosure()) DC = DC->getParent();"
-  // except that it's significantly more efficient to cast to a known
-  // decl type and call getDeclContext() than to call getParent().
-  while (isa<BlockDecl>(DC))
-    DC = cast<BlockDecl>(DC)->getDeclContext();
+Decl *Decl::getNonClosureContext() {
+  return ::getNonClosureContext(this);
+}
 
-  assert(!DC->isClosure());
-  return DC;
+Decl *DeclContext::getNonClosureAncestor() {
+  return ::getNonClosureContext(this);
 }
 
 //===----------------------------------------------------------------------===//

Modified: cfe/trunk/lib/CodeGen/CGBlocks.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGBlocks.cpp?rev=181000&r1=180999&r2=181000&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGBlocks.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGBlocks.cpp Fri May  3 02:33:41 2013
@@ -695,8 +695,8 @@ llvm::Value *CodeGenFunction::EmitBlockL
   bool isLambdaConv = blockInfo.getBlockDecl()->isConversionFromLambda();
   llvm::Constant *blockFn
     = CodeGenFunction(CGM, true).GenerateBlockFunction(CurGD, blockInfo,
-                                                 CurFuncDecl, LocalDeclMap,
-                                                 isLambdaConv);
+                                                       LocalDeclMap,
+                                                       isLambdaConv);
   blockFn = llvm::ConstantExpr::getBitCast(blockFn, VoidPtrTy);
 
   // If there is nothing to capture, we can emit this as a global block.
@@ -1034,7 +1034,7 @@ CodeGenModule::GetAddrOfGlobalBlock(cons
     llvm::DenseMap<const Decl*, llvm::Value*> LocalDeclMap;
     blockFn = CodeGenFunction(*this).GenerateBlockFunction(GlobalDecl(),
                                                            blockInfo,
-                                                           0, LocalDeclMap,
+                                                           LocalDeclMap,
                                                            false);
   }
   blockFn = llvm::ConstantExpr::getBitCast(blockFn, VoidPtrTy);
@@ -1088,7 +1088,6 @@ static llvm::Constant *buildGlobalBlock(
 llvm::Function *
 CodeGenFunction::GenerateBlockFunction(GlobalDecl GD,
                                        const CGBlockInfo &blockInfo,
-                                       const Decl *outerFnDecl,
                                        const DeclMapTy &ldm,
                                        bool IsLambdaConversionToBlock) {
   const BlockDecl *blockDecl = blockInfo.getBlockDecl();
@@ -1148,7 +1147,6 @@ CodeGenFunction::GenerateBlockFunction(G
   // Begin generating the function.
   StartFunction(blockDecl, fnType->getResultType(), fn, fnInfo, args,
                 blockInfo.getBlockExpr()->getBody()->getLocStart());
-  CurFuncDecl = outerFnDecl; // StartFunction sets this to blockDecl
 
   // Okay.  Undo some of what StartFunction did.
   
@@ -1184,23 +1182,6 @@ CodeGenFunction::GenerateBlockFunction(G
     CXXThisValue = Builder.CreateLoad(addr, "this");
   }
 
-  // LoadObjCSelf() expects there to be an entry for 'self' in LocalDeclMap;
-  // appease it.
-  if (const ObjCMethodDecl *method
-        = dyn_cast_or_null<ObjCMethodDecl>(CurFuncDecl)) {
-    const VarDecl *self = method->getSelfDecl();
-
-    // There might not be a capture for 'self', but if there is...
-    if (blockInfo.Captures.count(self)) {
-      const CGBlockInfo::Capture &capture = blockInfo.getCapture(self);
-
-      llvm::Value *selfAddr = Builder.CreateStructGEP(BlockPointer,
-                                                      capture.getIndex(),
-                                                      "block.captured-self");
-      LocalDeclMap[self] = selfAddr;
-    }
-  }
-
   // Also force all the constant captures.
   for (BlockDecl::capture_const_iterator ci = blockDecl->capture_begin(),
          ce = blockDecl->capture_end(); ci != ce; ++ci) {

Modified: cfe/trunk/lib/CodeGen/CGCall.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGCall.cpp?rev=181000&r1=180999&r2=181000&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGCall.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGCall.cpp Fri May  3 02:33:41 2013
@@ -1196,7 +1196,7 @@ void CodeGenFunction::EmitFunctionProlog
   // initialize the return value.  TODO: it might be nice to have
   // a more general mechanism for this that didn't require synthesized
   // return statements.
-  if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(CurFuncDecl)) {
+  if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(CurCodeDecl)) {
     if (FD->hasImplicitReturnZero()) {
       QualType RetTy = FD->getResultType().getUnqualifiedType();
       llvm::Type* LLVMTy = CGM.getTypes().ConvertType(RetTy);

Modified: cfe/trunk/lib/CodeGen/CGClass.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGClass.cpp?rev=181000&r1=180999&r2=181000&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGClass.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGClass.cpp Fri May  3 02:33:41 2013
@@ -290,7 +290,7 @@ llvm::Value *CodeGenFunction::GetVTTPara
     return 0;
   }
   
-  const CXXRecordDecl *RD = cast<CXXMethodDecl>(CurFuncDecl)->getParent();
+  const CXXRecordDecl *RD = cast<CXXMethodDecl>(CurCodeDecl)->getParent();
   const CXXRecordDecl *Base = cast<CXXMethodDecl>(GD.getDecl())->getParent();
 
   llvm::Value *VTT;
@@ -2232,10 +2232,10 @@ void CodeGenFunction::EmitLambdaBlockInv
 }
 
 void CodeGenFunction::EmitLambdaToBlockPointerBody(FunctionArgList &Args) {
-  if (cast<CXXMethodDecl>(CurFuncDecl)->isVariadic()) {
+  if (cast<CXXMethodDecl>(CurCodeDecl)->isVariadic()) {
     // FIXME: Making this work correctly is nasty because it requires either
     // cloning the body of the call operator or making the call operator forward.
-    CGM.ErrorUnsupported(CurFuncDecl, "lambda conversion to variadic function");
+    CGM.ErrorUnsupported(CurCodeDecl, "lambda conversion to variadic function");
     return;
   }
 

Modified: cfe/trunk/lib/CodeGen/CGExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGExpr.cpp?rev=181000&r1=180999&r2=181000&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGExpr.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGExpr.cpp Fri May  3 02:33:41 2013
@@ -2495,6 +2495,17 @@ LValue CodeGenFunction::EmitMemberExpr(c
   llvm_unreachable("Unhandled member declaration!");
 }
 
+/// Given that we are currently emitting a lambda, emit an l-value for
+/// one of its members.
+LValue CodeGenFunction::EmitLValueForLambdaField(const FieldDecl *Field) {
+  assert(cast<CXXMethodDecl>(CurCodeDecl)->getParent()->isLambda());
+  assert(cast<CXXMethodDecl>(CurCodeDecl)->getParent() == Field->getParent());
+  QualType LambdaTagType =
+    getContext().getTagDeclType(Field->getParent());
+  LValue LambdaLV = MakeNaturalAlignAddrLValue(CXXABIThisValue, LambdaTagType);
+  return EmitLValueForField(LambdaLV, Field);
+}
+
 LValue CodeGenFunction::EmitLValueForField(LValue base,
                                            const FieldDecl *field) {
   if (field->isBitField()) {

Modified: cfe/trunk/lib/CodeGen/CGObjC.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGObjC.cpp?rev=181000&r1=180999&r2=181000&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGObjC.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGObjC.cpp Fri May  3 02:33:41 2013
@@ -1401,8 +1401,10 @@ bool CodeGenFunction::IvarTypeWithAggrGC
 }
 
 llvm::Value *CodeGenFunction::LoadObjCSelf() {
-  const ObjCMethodDecl *OMD = cast<ObjCMethodDecl>(CurFuncDecl);
-  return Builder.CreateLoad(LocalDeclMap[OMD->getSelfDecl()], "self");
+  VarDecl *Self = cast<ObjCMethodDecl>(CurFuncDecl)->getSelfDecl();
+  DeclRefExpr DRE(Self, /*is enclosing local*/ (CurFuncDecl != CurCodeDecl),
+                  Self->getType(), VK_LValue, SourceLocation());
+  return EmitLoadOfScalar(EmitDeclRefLValue(&DRE));
 }
 
 QualType CodeGenFunction::TypeOfSelfObject() {

Modified: cfe/trunk/lib/CodeGen/CodeGenFunction.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenFunction.cpp?rev=181000&r1=180999&r2=181000&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CodeGenFunction.cpp (original)
+++ cfe/trunk/lib/CodeGen/CodeGenFunction.cpp Fri May  3 02:33:41 2013
@@ -471,7 +471,8 @@ void CodeGenFunction::EmitOpenCLKernelMe
   OpenCLKernelMetadata->addOperand(kernelMDNode);
 }
 
-void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
+void CodeGenFunction::StartFunction(GlobalDecl GD,
+                                    QualType RetTy,
                                     llvm::Function *Fn,
                                     const CGFunctionInfo &FnInfo,
                                     const FunctionArgList &Args,
@@ -479,7 +480,8 @@ void CodeGenFunction::StartFunction(Glob
   const Decl *D = GD.getDecl();
 
   DidCallStackSave = false;
-  CurCodeDecl = CurFuncDecl = D;
+  CurCodeDecl = D;
+  CurFuncDecl = (D ? D->getNonClosureContext() : 0);
   FnRetTy = RetTy;
   CurFn = Fn;
   CurFnInfo = &FnInfo;
@@ -578,12 +580,7 @@ void CodeGenFunction::StartFunction(Glob
                                         LambdaThisCaptureField);
       if (LambdaThisCaptureField) {
         // If this lambda captures this, load it.
-        QualType LambdaTagType =
-            getContext().getTagDeclType(LambdaThisCaptureField->getParent());
-        LValue LambdaLV = MakeNaturalAlignAddrLValue(CXXABIThisValue,
-                                                     LambdaTagType);
-        LValue ThisLValue = EmitLValueForField(LambdaLV,
-                                               LambdaThisCaptureField);
+        LValue ThisLValue = EmitLValueForLambdaField(LambdaThisCaptureField);
         CXXThisValue = EmitLoadOfLValue(ThisLValue).getScalarVal();
       }
     } else {

Modified: cfe/trunk/lib/CodeGen/CodeGenFunction.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenFunction.h?rev=181000&r1=180999&r2=181000&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CodeGenFunction.h (original)
+++ cfe/trunk/lib/CodeGen/CodeGenFunction.h Fri May  3 02:33:41 2013
@@ -579,8 +579,8 @@ public:
   typedef std::pair<llvm::Value *, llvm::Value *> ComplexPairTy;
   CGBuilderTy Builder;
 
-  /// CurFuncDecl - Holds the Decl for the current function or ObjC method.
-  /// This excludes BlockDecls.
+  /// CurFuncDecl - Holds the Decl for the current outermost
+  /// non-closure context.
   const Decl *CurFuncDecl;
   /// CurCodeDecl - This is the inner-most code context, which includes blocks.
   const Decl *CurCodeDecl;
@@ -1451,7 +1451,6 @@ public:
 
   llvm::Function *GenerateBlockFunction(GlobalDecl GD,
                                         const CGBlockInfo &Info,
-                                        const Decl *OuterFuncDecl,
                                         const DeclMapTy &ldm,
                                         bool IsLambdaConversionToBlock);
 
@@ -1482,7 +1481,8 @@ public:
 
   void GenerateCode(GlobalDecl GD, llvm::Function *Fn,
                     const CGFunctionInfo &FnInfo);
-  void StartFunction(GlobalDecl GD, QualType RetTy,
+  void StartFunction(GlobalDecl GD,
+                     QualType RetTy,
                      llvm::Function *Fn,
                      const CGFunctionInfo &FnInfo,
                      const FunctionArgList &Args,
@@ -2379,6 +2379,7 @@ public:
   llvm::Value *EmitIvarOffset(const ObjCInterfaceDecl *Interface,
                               const ObjCIvarDecl *Ivar);
   LValue EmitLValueForField(LValue Base, const FieldDecl* Field);
+  LValue EmitLValueForLambdaField(const FieldDecl *Field);
 
   /// EmitLValueForFieldInitialization - Like EmitLValueForField, except that
   /// if the Field is a reference, this will return the address of the reference

Modified: cfe/trunk/lib/Sema/SemaExprObjC.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprObjC.cpp?rev=181000&r1=180999&r2=181000&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExprObjC.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExprObjC.cpp Fri May  3 02:33:41 2013
@@ -1334,7 +1334,7 @@ bool Sema::CheckMessageArgumentTypes(Qua
 bool Sema::isSelfExpr(Expr *receiver) {
   // 'self' is objc 'self' in an objc method only.
   ObjCMethodDecl *method =
-    dyn_cast<ObjCMethodDecl>(CurContext->getNonClosureAncestor());
+    dyn_cast_or_null<ObjCMethodDecl>(CurContext->getNonClosureAncestor());
   if (!method) return false;
 
   receiver = receiver->IgnoreParenLValueCasts();

Modified: cfe/trunk/test/CodeGenObjC/debug-info-block-captured-self.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenObjC/debug-info-block-captured-self.m?rev=181000&r1=180999&r2=181000&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenObjC/debug-info-block-captured-self.m (original)
+++ cfe/trunk/test/CodeGenObjC/debug-info-block-captured-self.m Fri May  3 02:33:41 2013
@@ -59,7 +59,6 @@ typedef enum {
 // CHECK:        call void @llvm.dbg.declare(metadata !{i8* [[BLOCK_DESC]]}, metadata ![[BDMD:[0-9]+]])
 // CHECK:        %[[TMP1:.*]] = bitcast
 // CHECK-NEXT:   store
-// CHECK-NEXT:   %[[TMP2:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>* %[[TMP1]]
 // CHECK:        call void @llvm.dbg.declare(metadata !{<{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>** {{.*}}}, metadata ![[SELF:.*]])
 // make sure we are still in the same function
 // CHECK: define {{.*}}__copy_helper_block_

Modified: cfe/trunk/test/CodeGenObjC/debug-info-blocks.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenObjC/debug-info-blocks.m?rev=181000&r1=180999&r2=181000&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenObjC/debug-info-blocks.m (original)
+++ cfe/trunk/test/CodeGenObjC/debug-info-blocks.m Fri May  3 02:33:41 2013
@@ -7,11 +7,10 @@
 // CHECK: define {{.*}}_block_invoke
 // CHECK: %[[BLOCK:.*]] = bitcast i8* %.block_descriptor to <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>*, !dbg
 // CHECK-NEXT: store <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>* %[[BLOCK]], <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>** %[[ALLOCA:.*]], align
-// CHECK-NEXT: getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>* %[[BLOCK]], i32 0, i32 5
 // CHECK-NEXT: call void @llvm.dbg.declare(metadata !{<{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>** %[[ALLOCA]]}, metadata ![[SELF:[0-9]+]])
 // CHECK-NEXT: call void @llvm.dbg.declare(metadata !{%1** %d}, metadata ![[D:[0-9]+]])
-// CHECK: ![[SELF]] = {{.*}} [ DW_TAG_auto_variable ] [self] [line 52]
-// CHECK: ![[D]] = {{.*}} [d] [line 50]
+// CHECK: ![[SELF]] = {{.*}} [ DW_TAG_auto_variable ] [self] [line 51]
+// CHECK: ![[D]] = {{.*}} [d] [line 49]
 
 typedef unsigned int NSUInteger;
 

Modified: cfe/trunk/test/CodeGenObjCXX/lambda-expressions.mm
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenObjCXX/lambda-expressions.mm?rev=181000&r1=180999&r2=181000&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenObjCXX/lambda-expressions.mm (original)
+++ cfe/trunk/test/CodeGenObjCXX/lambda-expressions.mm Fri May  3 02:33:41 2013
@@ -38,6 +38,28 @@ void f2() { global = []{ return 3; }; }
 // ARC: define internal i32 @___Z2f2v_block_invoke
 // ARC: call i32 @"_ZZ2f2vENK3$_1clEv
 
+template <class T> void take_lambda(T &&lambda) { lambda(); }
+void take_block(void (^block)()) { block(); }
+
+// rdar://13800041
+ at interface A
+- (void) test;
+ at end
+ at interface B : A @end
+ at implementation B
+- (void) test {
+  take_block(^{
+      take_lambda([=]{
+          take_block(^{
+              take_lambda([=] {
+                  [super test];
+              });
+          });
+      });
+   });
+}
+ at end
+
 // ARC: attributes [[NUW]] = { nounwind{{.*}} }
 
 // MRC: attributes [[NUW]] = { nounwind{{.*}} }





More information about the cfe-commits mailing list