]> granicus.if.org Git - clang/commitdiff
Rewrite variable capture within lambda expressions and blocks,
authorDouglas Gregor <dgregor@apple.com>
Sat, 18 Feb 2012 09:37:24 +0000 (09:37 +0000)
committerDouglas Gregor <dgregor@apple.com>
Sat, 18 Feb 2012 09:37:24 +0000 (09:37 +0000)
eliminating a bunch of redundant code and properly modeling how the
captures of outside blocks/lambdas affect the types seen by inner
captures.

This new scheme makes two passes over the capturing scope stack. The
first pass goes up the stack (from innermost to outermost), assessing
whether the capture looks feasible and stopping when it either hits
the scope where the variable is declared or when it finds an existing
capture. The second pass then walks down the stack (from outermost to
innermost), capturing the variable at each step and updating the
captured type and the type that an expression referring to that
captured variable would see. It also checks type-specific
restrictions, such as the inability to capture an array within a
block. Note that only the first odr-use of each
variable needs to do the full walk; subsequent uses will find the
capture immediately, so multiple walks need not occur.

The same routine that builds the captures can also compute the type of
the captures without signaling errors and without actually performing
the capture. This functionality is used to determine the type of
declaration references as well as implementing the weird decltype((x))
rule within lambda expressions.

The capture code now explicitly takes sides in the debate over C++
core issue 1249, which concerns the type of captures within nested
lambdas. We opt to use the more permissive, more useful definition
implemented by GCC rather than the one implemented by EDG.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@150875 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Sema/ScopeInfo.h
include/clang/Sema/Sema.h
lib/Sema/SemaExpr.cpp
lib/Sema/SemaExprCXX.cpp
lib/Sema/SemaExprObjC.cpp
lib/Sema/SemaLambda.cpp
lib/Sema/TreeTransform.h
test/CXX/expr/expr.prim/expr.prim.lambda/blocks.cpp
test/CXX/expr/expr.prim/expr.prim.lambda/p16.cpp
test/CXX/expr/expr.prim/expr.prim.lambda/p18.cpp
test/SemaCXX/lambda-expressions.cpp

index 2ccb37010cf33f6839ed878664be4c9969b1f9bc..ceaf58669412d64b84baac4ccf51a583de24fa5c 100644 (file)
@@ -174,16 +174,23 @@ public:
     /// \brief The location of the ellipsis that expands a parameter pack.
     SourceLocation EllipsisLoc;
     
+    /// \brief The type as it was captured, which is in effect the type of the
+    /// non-static data member that would hold the capture.
+    QualType CaptureType;
+    
   public:
     Capture(VarDecl *Var, bool block, bool byRef, bool isNested, 
-            SourceLocation Loc, SourceLocation EllipsisLoc, Expr *Cpy)
+            SourceLocation Loc, SourceLocation EllipsisLoc, 
+            QualType CaptureType, Expr *Cpy)
       : VarAndKind(Var, block ? Cap_Block : byRef ? Cap_ByRef : Cap_ByCopy),
-        CopyExprAndNested(Cpy, isNested), Loc(Loc), EllipsisLoc(EllipsisLoc) {}
+        CopyExprAndNested(Cpy, isNested), Loc(Loc), EllipsisLoc(EllipsisLoc),
+        CaptureType(CaptureType){}
 
     enum IsThisCapture { ThisCapture };
-    Capture(IsThisCapture, bool isNested, SourceLocation Loc, Expr *Cpy)
+    Capture(IsThisCapture, bool isNested, SourceLocation Loc, 
+            QualType CaptureType, Expr *Cpy)
       : VarAndKind(0, Cap_This), CopyExprAndNested(Cpy, isNested), Loc(Loc),
-        EllipsisLoc() { }
+        EllipsisLoc(), CaptureType(CaptureType) { }
 
     bool isThisCapture() const { return VarAndKind.getInt() == Cap_This; }
     bool isVariableCapture() const { return !isThisCapture(); }
@@ -203,6 +210,11 @@ public:
     /// indicates that the capture is a pack expansion.
     SourceLocation getEllipsisLoc() const { return EllipsisLoc; }
     
+    /// \brief Retrieve the capture type for this capture, which is effectively
+    /// the type of the non-static data member in the lambda/block structure
+    /// that would store this capture.
+    QualType getCaptureType() const { return CaptureType; }
+    
     Expr *getCopyExpr() const {
       return CopyExprAndNested.getPointer();
     }
@@ -231,15 +243,18 @@ public:
   /// or null if unknown.
   QualType ReturnType;
 
-  void AddCapture(VarDecl *Var, bool isBlock, bool isByref, bool isNested,
-                  SourceLocation Loc, SourceLocation EllipsisLoc, Expr *Cpy) {
+  void addCapture(VarDecl *Var, bool isBlock, bool isByref, bool isNested,
+                  SourceLocation Loc, SourceLocation EllipsisLoc, 
+                  QualType CaptureType, Expr *Cpy) {
     Captures.push_back(Capture(Var, isBlock, isByref, isNested, Loc, 
-                               EllipsisLoc, Cpy));
+                               EllipsisLoc, CaptureType, Cpy));
     CaptureMap[Var] = Captures.size();
   }
 
-  void AddThisCapture(bool isNested, SourceLocation Loc, Expr *Cpy) {
-    Captures.push_back(Capture(Capture::ThisCapture, isNested, Loc, Cpy));
+  void addThisCapture(bool isNested, SourceLocation Loc, QualType CaptureType,
+                      Expr *Cpy) {
+    Captures.push_back(Capture(Capture::ThisCapture, isNested, Loc, CaptureType,
+                               Cpy));
     CXXThisCaptureIndex = Captures.size();
   }
 
index 87ec1aadef35eba2781ba552ec8c2004bfa60208..97cd27f7ed6da781f9c0478992dc63be3feb0b69 100644 (file)
@@ -2306,45 +2306,51 @@ public:
   void UpdateMarkingForLValueToRValue(Expr *E);
   void CleanupVarDeclMarking();
 
-  /// \brief Determine whether we can capture the given variable in
-  /// the given scope.
+  enum TryCaptureKind {
+    TryCapture_Implicit, TryCapture_ExplicitByVal, TryCapture_ExplicitByRef
+  };
+
+  /// \brief Try to capture the given variable.
   ///
-  /// \param Explicit Whether this is an explicit capture (vs. an
-  /// implicit capture).
+  /// \param Var The variable to capture.
   ///
-  /// \param Diagnose Diagnose errors that occur when attempting to perform
-  /// the capture.
+  /// \param Loc The location at which the capture occurs.
   ///
-  /// \param Var The variable to check for capture.
+  /// \param Kind The kind of capture, which may be implicit (for either a 
+  /// block or a lambda), or explicit by-value or by-reference (for a lambda).
   ///
-  /// \param Type Will be set to the type used to perform the capture.
+  /// \param EllipsisLoc The location of the ellipsis, if one is provided in
+  /// an explicit lambda capture.
   ///
-  /// \param FunctionScopesIndex Will be set to the index of the first 
-  /// scope in which capture will need to be performed.
+  /// \param BuildAndDiagnose Whether we are actually supposed to add the 
+  /// captures or diagnose errors. If false, this routine merely check whether
+  /// the capture can occur without performing the capture itself or complaining
+  /// if the variable cannot be captured.
   ///
-  /// \param Nested Whether this will be a nested capture.
-  bool canCaptureVariable(VarDecl *Var, SourceLocation Loc, bool Explicit,
-                          bool Diagnose, QualType &Type, 
-                          unsigned &FunctionScopesIndex, bool &Nested);
+  /// \param CaptureType Will be set to the type of the field used to capture
+  /// this variable in the innermost block or lambda. Only valid when the
+  /// variable can be captured.
+  ///
+  /// \param DeclRefType Will be set to the type of a refernce to the capture
+  /// from within the current scope. Only valid when the variable can be 
+  /// captured.
+  ///
+  /// \returns true if an error occurred (i.e., the variable cannot be
+  /// captured) and false if the capture succeeded.
+  bool tryCaptureVariable(VarDecl *Var, SourceLocation Loc, TryCaptureKind Kind,
+                          SourceLocation EllipsisLoc, bool BuildAndDiagnose, 
+                          QualType &CaptureType,
+                          QualType &DeclRefType);
 
+  /// \brief Try to capture the given variable.
+  bool tryCaptureVariable(VarDecl *Var, SourceLocation Loc,
+                          TryCaptureKind Kind = TryCapture_Implicit,
+                          SourceLocation EllipsisLoc = SourceLocation());
+  
   /// \brief Given a variable, determine the type that a reference to that
   /// variable will have in the given scope.
   QualType getCapturedDeclRefType(VarDecl *Var, SourceLocation Loc);
   
-  /// \brief Determine the type of the field that will capture the
-  /// given variable in a lambda expression.
-  ///
-  /// \param T The type of the variable being captured.
-  /// \param ByRef Whether we are capturing by reference or by value.
-  QualType getLambdaCaptureFieldType(QualType T, bool ByRef);
-
-  enum TryCaptureKind {
-    TryCapture_Implicit, TryCapture_ExplicitByVal, TryCapture_ExplicitByRef
-  };
-  void TryCaptureVar(VarDecl *var, SourceLocation loc,
-                     TryCaptureKind Kind = TryCapture_Implicit,
-                     SourceLocation EllipsisLoc = SourceLocation());
-
   void MarkDeclarationsReferencedInType(SourceLocation Loc, QualType T);
   void MarkDeclarationsReferencedInExpr(Expr *E);
 
index 38783eb86561bec6e88ba71b5e35937210414cdd..8e6b6c5c53c6c48d1286569f557f08d88e01ee55 100644 (file)
@@ -9541,56 +9541,12 @@ diagnoseUncapturableValueReference(Sema &S, SourceLocation loc,
   // capture.
 }
 
-static bool shouldAddConstForScope(CapturingScopeInfo *CSI, VarDecl *VD) {
-  if (VD->hasAttr<BlocksAttr>())
-    return false;
-  if (isa<BlockScopeInfo>(CSI))
-    return true;
-  if (LambdaScopeInfo *LSI = dyn_cast<LambdaScopeInfo>(CSI)) {
-    if (LSI->isCaptured(VD))
-      return LSI->getCapture(VD).isCopyCapture() && !LSI->Mutable;
-    
-    return LSI->ImpCaptureStyle == LambdaScopeInfo::ImpCap_LambdaByval &&
-           !LSI->Mutable;
-  }
-  return false;
-}
-
-QualType Sema::getLambdaCaptureFieldType(QualType T, bool ByRef) {
-  if (ByRef) {
-    // C++11 [expr.prim.lambda]p15:
-    //   An entity is captured by reference if it is implicitly or
-    //   explicitly captured but not captured by copy. It is
-    //   unspecified whether additional unnamed non-static data
-    //   members are declared in the closure type for entities
-    //   captured by reference.
-    return Context.getLValueReferenceType(T.getNonReferenceType());
-  }
-
-  // C++11 [expr.prim.lambda]p14:
-  //   For each entity captured by copy, an unnamed non-static
-  //   data member is declared in the closure type. The
-  //   declaration order of these members is unspecified. The type
-  //   of such a data member is the type of the corresponding
-  //   captured entity if the entity is not a reference to an
-  //   object, or the referenced type otherwise. [Note: If the
-  //   captured entity is a reference to a function, the
-  //   corresponding data member is also a reference to a
-  //   function. - end note ]
-  if (const ReferenceType *RefType = T->getAs<ReferenceType>()) {
-    if (!RefType->getPointeeType()->isFunctionType())
-      return RefType->getPointeeType();
-  }
-
-  return T;
-}
-
 /// \brief Capture the given variable in the given lambda expression.
 static ExprResult captureInLambda(Sema &S, LambdaScopeInfo *LSI,
-                                  VarDecl *Var, QualType Type, 
-                                  SourceLocation Loc, bool ByRef) {
+                                  VarDecl *Var, QualType FieldType, 
+                                  QualType DeclRefType,
+                                  SourceLocation Loc) {
   CXXRecordDecl *Lambda = LSI->Lambda;
-  QualType FieldType = S.getLambdaCaptureFieldType(Type, ByRef);
 
   // Build the non-static data member.
   FieldDecl *Field
@@ -9618,8 +9574,7 @@ static ExprResult captureInLambda(Sema &S, LambdaScopeInfo *LSI,
   // C++ [expr.prim.labda]p12:
   //   An entity captured by a lambda-expression is odr-used (3.2) in
   //   the scope containing the lambda-expression.
-  Expr *Ref = new (S.Context) DeclRefExpr(Var, Type.getNonReferenceType(),
-                                          VK_LValue, Loc);
+  Expr *Ref = new (S.Context) DeclRefExpr(Var, DeclRefType, VK_LValue, Loc);
   Var->setUsed(true);
 
   // When the field has array type, create index variables for each
@@ -9707,20 +9662,27 @@ static ExprResult captureInLambda(Sema &S, LambdaScopeInfo *LSI,
   return Result;
 }
 
-bool Sema::canCaptureVariable(VarDecl *Var, SourceLocation Loc, bool Explicit,
-                              bool Diagnose, QualType &Type, 
-                              unsigned &FunctionScopesIndex, bool &Nested) {
-  Type = Var->getType();
-  FunctionScopesIndex = FunctionScopes.size() - 1;
-  Nested = false;
+bool Sema::tryCaptureVariable(VarDecl *Var, SourceLocation Loc, 
+                              TryCaptureKind Kind, SourceLocation EllipsisLoc,
+                              bool BuildAndDiagnose, 
+                              QualType &CaptureType,
+                              QualType &DeclRefType) {
+  bool Nested = false;
   
   DeclContext *DC = CurContext;
-  if (Var->getDeclContext() == DC) return false;
-  if (!Var->hasLocalStorage()) return false;
+  if (Var->getDeclContext() == DC) return true;
+  if (!Var->hasLocalStorage()) return true;
 
   bool HasBlocksAttr = Var->hasAttr<BlocksAttr>();
 
-  // Figure out whether we can capture the variable.
+  // Walk up the stack to determine whether we can capture the variable,
+  // performing the "simple" checks that don't depend on type. We stop when
+  // we've either hit the declared scope of the variable or find an existing
+  // capture of that variable.
+  CaptureType = Var->getType();
+  DeclRefType = CaptureType.getNonReferenceType();
+  bool Explicit = (Kind != TryCapture_Implicit);
+  unsigned FunctionScopesIndex = FunctionScopes.size() - 1;
   do {
     // Only block literals and lambda expressions can capture; other
     // scopes don't work.
@@ -9732,9 +9694,9 @@ bool Sema::canCaptureVariable(VarDecl *Var, SourceLocation Loc, bool Explicit,
              cast<CXXRecordDecl>(DC->getParent())->isLambda())
       ParentDC = DC->getParent()->getParent();
     else {
-      if (Diagnose)
+      if (BuildAndDiagnose)
         diagnoseUncapturableValueReference(*this, Loc, Var, DC);
-      return false;
+      return true;
     }
 
     CapturingScopeInfo *CSI =
@@ -9742,32 +9704,40 @@ bool Sema::canCaptureVariable(VarDecl *Var, SourceLocation Loc, bool Explicit,
 
     // Check whether we've already captured it.
     if (CSI->CaptureMap.count(Var)) {
-      // If we found a capture, any subcaptures are nested
+      // If we found a capture, any subcaptures are nested.
       Nested = true;
-
-      if (shouldAddConstForScope(CSI, Var))
-        Type.addConst();
+      
+      // Retrieve the capture type for this variable.
+      CaptureType = CSI->getCapture(Var).getCaptureType();
+      
+      // Compute the type of an expression that refers to this variable.
+      DeclRefType = CaptureType.getNonReferenceType();
+      
+      const CapturingScopeInfo::Capture &Cap = CSI->getCapture(Var);
+      if (Cap.isCopyCapture() &&
+          !(isa<LambdaScopeInfo>(CSI) && cast<LambdaScopeInfo>(CSI)->Mutable))
+        DeclRefType.addConst();
       break;
     }
 
     bool IsBlock = isa<BlockScopeInfo>(CSI);
-    bool IsLambda = isa<LambdaScopeInfo>(CSI);
+    bool IsLambda = !IsBlock;
 
     // Lambdas are not allowed to capture unnamed variables
     // (e.g. anonymous unions).
     // FIXME: The C++11 rule don't actually state this explicitly, but I'm
     // assuming that's the intent.
     if (IsLambda && !Var->getDeclName()) {
-      if (Diagnose) {
+      if (BuildAndDiagnose) {
         Diag(Loc, diag::err_lambda_capture_anonymous_var);
         Diag(Var->getLocation(), diag::note_declared_at);
       }
-      return false;
+      return true;
     }
 
     // Prohibit variably-modified types; they're difficult to deal with.
-    if (Type->isVariablyModifiedType()) {
-      if (Diagnose) {
+    if (Var->getType()->isVariablyModifiedType()) {
+      if (BuildAndDiagnose) {
         if (IsBlock)
           Diag(Loc, diag::err_ref_vm_type);
         else
@@ -9775,41 +9745,31 @@ bool Sema::canCaptureVariable(VarDecl *Var, SourceLocation Loc, bool Explicit,
         Diag(Var->getLocation(), diag::note_previous_decl) 
           << Var->getDeclName();
       }
-      return false;
-    }
-
-    // Blocks are not allowed to capture arrays.
-    if (IsBlock && Type->isArrayType()) {
-      if (Diagnose) {
-        Diag(Loc, diag::err_ref_array_type);
-        Diag(Var->getLocation(), diag::note_previous_decl) 
-          << Var->getDeclName();
-      }
-      return false;
+      return true;
     }
 
     // Lambdas are not allowed to capture __block variables; they don't
     // support the expected semantics.
     if (IsLambda && HasBlocksAttr) {
-      if (Diagnose) {
+      if (BuildAndDiagnose) {
         Diag(Loc, diag::err_lambda_capture_block) 
           << Var->getDeclName();
         Diag(Var->getLocation(), diag::note_previous_decl) 
           << Var->getDeclName();
       }
-      return false;
+      return true;
     }
 
     if (CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_None && !Explicit) {
       // No capture-default
-      if (Diagnose) {
+      if (BuildAndDiagnose) {
         Diag(Loc, diag::err_lambda_impcap) << Var->getDeclName();
         Diag(Var->getLocation(), diag::note_previous_decl) 
           << Var->getDeclName();
         Diag(cast<LambdaScopeInfo>(CSI)->Lambda->getLocStart(),
              diag::note_lambda_decl);
       }
-      return false;
+      return true;
     }
 
     FunctionScopesIndex--;
@@ -9817,126 +9777,175 @@ bool Sema::canCaptureVariable(VarDecl *Var, SourceLocation Loc, bool Explicit,
     Explicit = false;
   } while (!Var->getDeclContext()->Equals(DC));
 
-  ++FunctionScopesIndex;
-  return !Type->isVariablyModifiedType();
-}
+  // Walk back down the scope stack, computing the type of the capture at
+  // each step, checking type-specific requirements, and adding captures if
+  // requested.
+  for (unsigned I = ++FunctionScopesIndex, N = FunctionScopes.size(); I != N; 
+       ++I) {
+    CapturingScopeInfo *CSI = cast<CapturingScopeInfo>(FunctionScopes[I]);
+    
+    // Compute the type of the capture and of a reference to the capture within
+    // this scope.
+    if (isa<BlockScopeInfo>(CSI)) {
+      Expr *CopyExpr = 0;
+      bool ByRef = false;
+      
+      // Blocks are not allowed to capture arrays.
+      if (CaptureType->isArrayType()) {
+        if (BuildAndDiagnose) {
+          Diag(Loc, diag::err_ref_array_type);
+          Diag(Var->getLocation(), diag::note_previous_decl) 
+          << Var->getDeclName();
+        }
+        return true;
+      }
 
-QualType Sema::getCapturedDeclRefType(VarDecl *Var, SourceLocation Loc) {
-  QualType T = Var->getType().getNonReferenceType();
-  unsigned FunctionScopesIndex;
-  bool Nested;
-  // Determine whether we can capture this variable.
-  if (!canCaptureVariable(Var, Loc, /*Explicit=*/false, /*Diagnose=*/false, 
-                          T, FunctionScopesIndex, Nested))
-    return QualType();
+      if (HasBlocksAttr || CaptureType->isReferenceType()) {
+        // Block capture by reference does not change the capture or
+        // declaration reference types.
+        ByRef = true;
+      } else {
+        // Block capture by copy introduces 'const'.
+        CaptureType = CaptureType.getNonReferenceType().withConst();
+        DeclRefType = CaptureType;
+                
+        if (getLangOptions().CPlusPlus && BuildAndDiagnose) {
+          if (const RecordType *Record = DeclRefType->getAs<RecordType>()) {
+            // The capture logic needs the destructor, so make sure we mark it.
+            // Usually this is unnecessary because most local variables have
+            // their destructors marked at declaration time, but parameters are
+            // an exception because it's technically only the call site that
+            // actually requires the destructor.
+            if (isa<ParmVarDecl>(Var))
+              FinalizeVarWithDestructor(Var, Record);
+            
+            // According to the blocks spec, the capture of a variable from
+            // the stack requires a const copy constructor.  This is not true
+            // of the copy/move done to move a __block variable to the heap.
+            Expr *DeclRef = new (Context) DeclRefExpr(Var, 
+                                                      DeclRefType.withConst(), 
+                                                      VK_LValue, Loc);
+            ExprResult Result
+              = PerformCopyInitialization(
+                  InitializedEntity::InitializeBlock(Var->getLocation(),
+                                                     CaptureType, false),
+                  Loc, Owned(DeclRef));
+            
+            // Build a full-expression copy expression if initialization
+            // succeeded and used a non-trivial constructor.  Recover from
+            // errors by pretending that the copy isn't necessary.
+            if (!Result.isInvalid() &&
+                !cast<CXXConstructExpr>(Result.get())->getConstructor()
+                   ->isTrivial()) {
+              Result = MaybeCreateExprWithCleanups(Result);
+              CopyExpr = Result.take();
+            }
+          }
+        }
+      }
+
+      // Actually capture the variable.
+      if (BuildAndDiagnose)
+        CSI->addCapture(Var, HasBlocksAttr, ByRef, Nested, Loc, 
+                        SourceLocation(), CaptureType, CopyExpr);
+      Nested = true;
+      continue;
+    } 
     
-  // Outer lambda scopes may have an effect on the type of a
-  // capture. Walk the captures outside-in to determine
-  // whether they can add 'const' to a capture by copy.
-  T = Var->getType().getNonReferenceType();
-  if (FunctionScopesIndex == FunctionScopes.size())
-    --FunctionScopesIndex;
-  for (unsigned I = FunctionScopesIndex, E = FunctionScopes.size();
-       I != E; ++I) {
-    CapturingScopeInfo *CSI = dyn_cast<LambdaScopeInfo>(FunctionScopes[I]);
-    if (!CSI)
-      break;
+    LambdaScopeInfo *LSI = cast<LambdaScopeInfo>(CSI);
     
-    if (shouldAddConstForScope(CSI, Var))
-      T.addConst();
-  }
-  
-  return T;
-}
-
-// Check if the variable needs to be captured; if so, try to perform
-// the capture.
-void Sema::TryCaptureVar(VarDecl *var, SourceLocation loc,
-                         TryCaptureKind Kind, SourceLocation EllipsisLoc) {
-  QualType type;
-  unsigned functionScopesIndex;
-  bool Nested;
-  // Determine whether we can capture this variable, and where to
-  // start capturing.
-  if (!canCaptureVariable(var, loc, /*Explicit=*/Kind != TryCapture_Implicit,
-                          /*Diagnose=*/true, type, functionScopesIndex, Nested))
-    return;
-
-  bool hasBlocksAttr = var->hasAttr<BlocksAttr>();
-
-  for (unsigned i = functionScopesIndex,
-                e = FunctionScopes.size(); i != e; ++i) {
-    CapturingScopeInfo *CSI = cast<CapturingScopeInfo>(FunctionScopes[i]);
-    bool isLambda = isa<LambdaScopeInfo>(CSI);
-
-    bool byRef;
-    bool isInnermostCapture = (i == e - 1);
-    if (isInnermostCapture && Kind == TryCapture_ExplicitByVal) {
-      byRef = false;
-    } else if (isInnermostCapture && Kind == TryCapture_ExplicitByRef) {
-      byRef = true;
-    } else if (CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_LambdaByval) {
-      // capture-default '='
-      byRef = false;
-    } else if (CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_LambdaByref) {
-      // capture-default '&'
-      byRef = true;
+    // Determine whether we are capturing by reference or by value.
+    bool ByRef = false;
+    if (I == N - 1 && Kind != TryCapture_Implicit) {
+      ByRef = (Kind == TryCapture_ExplicitByRef);
     } else {
-      // A block captures __block variables in a special __block fashion, 
-      // variables of reference type by reference (in the sense of
-      // [expr.prim.lambda]), and other non-__block variables by copy.
-      assert(CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_Block);
-      byRef = hasBlocksAttr || type->isReferenceType();
-    }
-
-    // Build a copy expression if we are capturing by copy into a
-    // block and the copy might be non-trivial.
-    Expr *copyExpr = 0;
-    const RecordType *rtype;
-    if (isLambda) {
-      ExprResult Result = captureInLambda(*this, cast<LambdaScopeInfo>(CSI), 
-                                          var, type, loc, byRef);
-      if (!Result.isInvalid())
-        copyExpr = Result.take();
-    } else if (!byRef && getLangOptions().CPlusPlus &&
-        (rtype = type.getNonReferenceType()->getAs<RecordType>())) {
-      // The capture logic needs the destructor, so make sure we mark it.
-      // Usually this is unnecessary because most local variables have
-      // their destructors marked at declaration time, but parameters are
-      // an exception because it's technically only the call site that
-      // actually requires the destructor.
-      if (isa<ParmVarDecl>(var))
-        FinalizeVarWithDestructor(var, rtype);
-
-      // According to the blocks spec, the capture of a variable from
-      // the stack requires a const copy constructor.  This is not true
-      // of the copy/move done to move a __block variable to the heap.
-      type.addConst();
-
-      Expr *declRef = new (Context) DeclRefExpr(var, type, VK_LValue, loc);
-      ExprResult result =
-        PerformCopyInitialization(
-                        InitializedEntity::InitializeBlock(var->getLocation(),
-                                                           type, false),
-                                    loc, Owned(declRef));
-
-      // Build a full-expression copy expression if initialization
-      // succeeded and used a non-trivial constructor.  Recover from
-      // errors by pretending that the copy isn't necessary.
-      if (!result.isInvalid() &&
-          !cast<CXXConstructExpr>(result.get())->getConstructor()->isTrivial()) {
-        result = MaybeCreateExprWithCleanups(result);
-        copyExpr = result.take();
+      ByRef = (LSI->ImpCaptureStyle == LambdaScopeInfo::ImpCap_LambdaByref);
+    }
+    
+    // Compute the type of the field that will capture this variable.
+    if (ByRef) {
+      // C++11 [expr.prim.lambda]p15:
+      //   An entity is captured by reference if it is implicitly or
+      //   explicitly captured but not captured by copy. It is
+      //   unspecified whether additional unnamed non-static data
+      //   members are declared in the closure type for entities
+      //   captured by reference.
+      //
+      // FIXME: It is not clear whether we want to build an lvalue reference
+      // to the DeclRefType or to CaptureType.getNonReferenceType(). GCC appears
+      // to do the former, while EDG does the latter. Core issue 1249 will 
+      // clarify, but for now we follow GCC because it's a more permissive and
+      // easily defensible position.
+      CaptureType = Context.getLValueReferenceType(DeclRefType);
+    } else {
+      // C++11 [expr.prim.lambda]p14:
+      //   For each entity captured by copy, an unnamed non-static
+      //   data member is declared in the closure type. The
+      //   declaration order of these members is unspecified. The type
+      //   of such a data member is the type of the corresponding
+      //   captured entity if the entity is not a reference to an
+      //   object, or the referenced type otherwise. [Note: If the
+      //   captured entity is a reference to a function, the
+      //   corresponding data member is also a reference to a
+      //   function. - end note ]
+      if (const ReferenceType *RefType = CaptureType->getAs<ReferenceType>()){
+        if (!RefType->getPointeeType()->isFunctionType())
+          CaptureType = RefType->getPointeeType();
       }
     }
 
-    CSI->AddCapture(var, hasBlocksAttr, byRef, Nested, loc, EllipsisLoc, 
-                    copyExpr);
-
+    // Capture this variable in the lambda.
+    Expr *CopyExpr = 0;
+    if (BuildAndDiagnose) {
+      ExprResult Result = captureInLambda(*this, LSI, Var, CaptureType,
+                                          DeclRefType, Loc);
+      if (!Result.isInvalid())
+        CopyExpr = Result.take();
+    }
+    
+    // Compute the type of a reference to this captured variable.
+    if (ByRef)
+      DeclRefType = CaptureType.getNonReferenceType();
+    else {
+      // C++ [expr.prim.lambda]p5:
+      //   The closure type for a lambda-expression has a public inline 
+      //   function call operator [...]. This function call operator is 
+      //   declared const (9.3.1) if and only if the lambda-expression’s 
+      //   parameter-declaration-clause is not followed by mutable.
+      DeclRefType = CaptureType.getNonReferenceType();
+      if (!LSI->Mutable && !CaptureType->isReferenceType())
+        DeclRefType.addConst();      
+    }
+    
+    // Add the capture.
+    if (BuildAndDiagnose)
+      CSI->addCapture(Var, /*IsBlock=*/false, ByRef, Nested, Loc,
+                      EllipsisLoc, CaptureType, CopyExpr);
     Nested = true;
-    if (shouldAddConstForScope(CSI, var))
-      type.addConst();
   }
+
+  return false;
+}
+
+bool Sema::tryCaptureVariable(VarDecl *Var, SourceLocation Loc,
+                              TryCaptureKind Kind, SourceLocation EllipsisLoc) {  
+  QualType CaptureType;
+  QualType DeclRefType;
+  return tryCaptureVariable(Var, Loc, Kind, EllipsisLoc,
+                            /*BuildAndDiagnose=*/true, CaptureType,
+                            DeclRefType);
+}
+
+QualType Sema::getCapturedDeclRefType(VarDecl *Var, SourceLocation Loc) {
+  QualType CaptureType;
+  QualType DeclRefType;
+  
+  // Determine whether we can capture this variable.
+  if (tryCaptureVariable(Var, Loc, TryCapture_Implicit, SourceLocation(),
+                         /*BuildAndDiagnose=*/false, CaptureType, DeclRefType))
+    return QualType();
+
+  return DeclRefType;
 }
 
 static void MarkVarDeclODRUsed(Sema &SemaRef, VarDecl *Var,
@@ -9950,7 +9959,7 @@ static void MarkVarDeclODRUsed(Sema &SemaRef, VarDecl *Var,
     if (old.isInvalid()) old = Loc;
   }
 
-  SemaRef.TryCaptureVar(Var, Loc);
+  SemaRef.tryCaptureVariable(Var, Loc);
 
   Var->setUsed(true);
 }
index 80078207da6d63a93b7b74e906a6709224f889ad..5ec96be4870794d1c9c38621e130534379f447d3 100644 (file)
@@ -710,9 +710,9 @@ void Sema::CheckCXXThisCapture(SourceLocation Loc, bool Explicit) {
        NumClosures; --idx, --NumClosures) {
     CapturingScopeInfo *CSI = cast<CapturingScopeInfo>(FunctionScopes[idx]);
     Expr *ThisExpr = 0;
+    QualType ThisTy = getCurrentThisType();
     if (LambdaScopeInfo *LSI = dyn_cast<LambdaScopeInfo>(CSI)) {
       // For lambda expressions, build a field and an initializing expression.
-      QualType ThisTy = getCurrentThisType();
       CXXRecordDecl *Lambda = LSI->Lambda;
       FieldDecl *Field
         = FieldDecl::Create(Context, Lambda, Loc, Loc, 0, ThisTy,
@@ -724,7 +724,7 @@ void Sema::CheckCXXThisCapture(SourceLocation Loc, bool Explicit) {
       ThisExpr = new (Context) CXXThisExpr(Loc, ThisTy, /*isImplicit=*/true);
     }
     bool isNested = NumClosures > 1;
-    CSI->AddThisCapture(isNested, Loc, ThisExpr);
+    CSI->addThisCapture(isNested, Loc, ThisTy, ThisExpr);
   }
 }
 
index 133c2d3de91cb06598054f67fb2446fffa21f54c..e0420c1faff98fc82702cddd91d50ca1d673b252 100644 (file)
@@ -247,7 +247,7 @@ ObjCMethodDecl *Sema::tryCaptureObjCSelf(SourceLocation Loc) {
   if (!method)
     return 0;
 
-  TryCaptureVar(method->getSelfDecl(), Loc);
+  tryCaptureVariable(method->getSelfDecl(), Loc);
 
   return method;
 }
index 2af946288f4032bc312f50ffff84ccbfadfc34ad..1a362d48e282cc12fe63a7735c48e288ffed4ed4 100644 (file)
@@ -288,7 +288,7 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
     //   variable with automatic storage duration declared in the reaching 
     //   scope of the local lambda expression.
     // 
-    // Note that the 'reaching scope' check happens in TryCaptureVar.
+    // Note that the 'reaching scope' check happens in tryCaptureVariable().
     VarDecl *Var = R.getAsSingle<VarDecl>();
     if (!Var) {
       Diag(C->Loc, diag::err_capture_does_not_name_variable) << C->Id;
@@ -332,7 +332,7 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
     
     TryCaptureKind Kind = C->Kind == LCK_ByRef ? TryCapture_ExplicitByRef :
                                                  TryCapture_ExplicitByVal;
-    TryCaptureVar(Var, C->Loc, Kind, EllipsisLoc);
+    tryCaptureVariable(Var, C->Loc, Kind, EllipsisLoc);
   }
   finishLambdaExplicitCaptures(LSI);
 
index 88173555e927f7bee5f7d0d835fd61cb553fb5e5..69e09e5ac3e6a6453112690fffd44fe75dbdbbb8 100644 (file)
@@ -7735,7 +7735,7 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
           }
           
           // Capture the transformed variable.
-          getSema().TryCaptureVar(CapturedVar, C->getLocation(), Kind);          
+          getSema().tryCaptureVariable(CapturedVar, C->getLocation(), Kind);          
         }          
         continue;
       }
@@ -7753,7 +7753,7 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
     }
   
     // Capture the transformed variable.
-    getSema().TryCaptureVar(CapturedVar, C->getLocation(), Kind);
+    getSema().tryCaptureVariable(CapturedVar, C->getLocation(), Kind);
   }
   if (!FinishedExplicitCaptures)
     getSema().finishLambdaExplicitCaptures(LSI);
index 3301b29135d6bc662a20bf60123bf566c5c9476d..f6a8db23e9b180ff29402c589d31a4fc0e9db300 100644 (file)
@@ -33,3 +33,25 @@ void conversion_to_block_init(ConstCopyConstructorBoom<int> boom,
   const auto& lambda2([=] { boom2.foo(); }); // expected-note{{in instantiation of member function}}
   void (^block)(void) = lambda2;
 }
+
+
+void nesting() {
+  int array[7]; // expected-note 2{{'array' declared here}}
+  [=] () mutable {
+    [&] {
+      ^ {
+        int i = array[2];
+        i += array[3];
+      }();
+    }();
+  }();
+
+  [&] {
+    [=] () mutable {
+      ^ {
+        int i = array[2]; // expected-error{{cannot refer to declaration with an array type inside block}}
+        i += array[3]; // expected-error{{cannot refer to declaration with an array type inside block}}
+      }();
+    }();
+  }();
+}
index 1723ee39fd7bd6ca9230057ee8b5235c3e167bda..0cf01ade4313b7801cc8927368c070bc631eaf10 100644 (file)
@@ -2,7 +2,7 @@
 
 
 struct X {
-  X(const X&) = delete; // expected-note{{explicitly marked deleted}}
+  X(const X&) = delete; // expected-note 2{{explicitly marked deleted}}
   X(X&);
 };
 
@@ -10,10 +10,31 @@ void test_capture(X x) {
   [x] { }(); // okay: non-const copy ctor
 
   [x] {
-    [x] { // expected-error{{call to deleted constructor of 'const X'}}
+    [x] { // expected-error{{call to deleted constructor of 'X'}}
+    }();
+  }();
+
+  [x] {
+    [&x] {
+      [x] { // expected-error{{call to deleted constructor of 'const X'}}
+      }();
     }();
   }();
 
   int a; 
-  [=]{ [&] { int&x = a; }(); }(); // expected-error{{binding of reference to type 'int' to a value of type 'const int' drops qualifiers}}
+  [=]{ 
+    [&] { 
+      int &x = a;  // expected-error{{binding of reference to type 'int' to a value of type 'const int' drops qualifiers}}
+      int &x2 = a;  // expected-error{{binding of reference to type 'int' to a value of type 'const int' drops qualifiers}}
+    }(); 
+  }(); 
+
+  [=]{ 
+    [&a] { 
+      [&] { 
+        int &x = a;  // expected-error{{binding of reference to type 'int' to a value of type 'const int' drops qualifiers}}
+        int &x2 = a;  // expected-error{{binding of reference to type 'int' to a value of type 'const int' drops qualifiers}}
+      }();
+    }(); 
+  }(); 
 }
index e9356136c7bda3d7c609776b4397b79a6a298eec..930a4b32fa06cd128e7499463b8375b5468fe9d4 100644 (file)
@@ -34,8 +34,8 @@ void f3() {
   [=] {
     [=] () mutable {
       static_assert(is_same<decltype(x), float>::value, "should be float");
-      static_assert(is_same<decltype((x)), const float&>::value, 
-                    "should be const float&");
+      static_assert(is_same<decltype((x)), float&>::value, 
+                    "should be float&");
     }();
   }();
 
index afbf9a1fffc1f4a0fe6129462df90ae2c56975af..b54da37014b3cd433c86c41025d1e3ccd3927e3d 100644 (file)
@@ -78,7 +78,7 @@ namespace ImplicitCapture {
     struct G { G(); G(G&); int a; }; // expected-note 6 {{not viable}}
     G g;
     [=]() { const G* gg = &g; return gg->a; }; // expected-warning{{omitted result type}}
-    [=]() { return [=]{ const G* gg = &g; return gg->a; }(); }; // expected-error {{no matching constructor for initialization of 'const ImplicitCapture::G'}}  \
+    [=]() { return [=]{ const G* gg = &g; return gg->a; }(); }; // expected-error {{no matching constructor for initialization of 'ImplicitCapture::G'}}  \
     // expected-warning{{omitted result type}}
     (void)^{ return [=]{ const G* gg = &g; return gg->a; }(); }; // expected-error 2 {{no matching constructor for initialization of 'const ImplicitCapture::G'}}  \
     // expected-warning{{omitted result type}}