]> granicus.if.org Git - clang/commitdiff
Alternate fix to PR12248: put Sema in charge of special-casing
authorJohn McCall <rjmccall@apple.com>
Tue, 13 Mar 2012 00:37:01 +0000 (00:37 +0000)
committerJohn McCall <rjmccall@apple.com>
Tue, 13 Mar 2012 00:37:01 +0000 (00:37 +0000)
the diagnostic for assigning to a copied block capture.  This has
the pleasant side-effect of letting us special-case the diagnostic
for assigning to a copied lambda capture as well, without introducing
a new non-modifiable enumerator for it.

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

include/clang/AST/Expr.h
include/clang/Basic/DiagnosticSemaKinds.td
lib/AST/ExprClassification.cpp
lib/Sema/SemaExpr.cpp
test/SemaCXX/lambda-expressions.cpp

index 294112bc3a637d1d2c59d84edd9c4af32b54f7b9..e22c44d65a1436cd58db83d7730b6375782fcf4f 100644 (file)
@@ -236,7 +236,6 @@ public:
     MLV_IncompleteType,
     MLV_ConstQualified,
     MLV_ArrayType,
-    MLV_NotBlockQualified,
     MLV_ReadonlyProperty,
     MLV_NoSetterProperty,
     MLV_MemberFunction,
@@ -272,7 +271,6 @@ public:
       CM_RValue, // Not modifiable because it's an rvalue
       CM_Function, // Not modifiable because it's a function; C++ only
       CM_LValueCast, // Same as CM_RValue, but indicates GCC cast-as-lvalue ext
-      CM_NotBlockQualified, // Not captured in the closure
       CM_NoSetterProperty,// Implicit assignment to ObjC property without setter
       CM_ConstQualified,
       CM_ArrayType,
index 034741ed88978bf85f2c728be4bd1ec4f6db5376..bdad36a982c761fb17d4ca9cfe6195a88e24f2b1 100644 (file)
@@ -4387,6 +4387,8 @@ def err_typecheck_duplicate_vector_components_not_mlvalue : Error<
   "vector is not assignable (contains duplicate components)">;
 def err_block_decl_ref_not_modifiable_lvalue : Error<
   "variable is not assignable (missing __block type specifier)">;
+def err_lambda_decl_ref_not_modifiable_lvalue : Error<
+  "variable is not assignable (captured by copy)">;
 def err_typecheck_call_not_function : Error<
   "called object type %0 is not a function or function pointer">;
 def err_call_incomplete_return : Error<
index 6697c9d7da6039e724dbfd9544acb694f1b09503..b091e19a8a95aa821902d87af90cdc6f69196c19 100644 (file)
@@ -567,18 +567,8 @@ static Cl::ModifiableType IsModifiable(ASTContext &Ctx, const Expr *E,
 
   CanQualType CT = Ctx.getCanonicalType(E->getType());
   // Const stuff is obviously not modifiable.
-  if (CT.isConstQualified()) {
-    // Special-case variables captured by blocks to get an improved
-    // diagnostic.
-    if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
-      if (DRE->refersToEnclosingLocal() &&
-          isa<VarDecl>(DRE->getDecl()) &&
-          cast<VarDecl>(DRE->getDecl())->hasLocalStorage() &&
-          !DRE->getDecl()->hasAttr<BlocksAttr>())
-        return Cl::CM_NotBlockQualified;
-    }
+  if (CT.isConstQualified())
     return Cl::CM_ConstQualified;
-  }
 
   // Arrays are not modifiable, only their elements are.
   if (CT->isArrayType())
@@ -645,7 +635,6 @@ Expr::isModifiableLvalue(ASTContext &Ctx, SourceLocation *Loc) const {
   case Cl::CM_Function: return MLV_NotObjectType;
   case Cl::CM_LValueCast:
     llvm_unreachable("CM_LValueCast and CL_LValue don't match");
-  case Cl::CM_NotBlockQualified: return MLV_NotBlockQualified;
   case Cl::CM_NoSetterProperty: return MLV_NoSetterProperty;
   case Cl::CM_ConstQualified: return MLV_ConstQualified;
   case Cl::CM_ArrayType: return MLV_ArrayType;
index 8aac35ee69ba9525f8bcbb2ed0b18d1ca2c36e1c..ae5257fecbd5573f80ea04b4c681f6e7ba961ed2 100644 (file)
@@ -7127,6 +7127,32 @@ static bool IsReadonlyMessage(Expr *E, Sema &S) {
   return Base->getMethodDecl() != 0;
 }
 
+/// Is the given expression (which must be 'const') a reference to a
+/// variable which was originally non-const, but which has become
+/// 'const' due to being captured within a block?
+enum NonConstCaptureKind { NCCK_None, NCCK_Block, NCCK_Lambda };
+static NonConstCaptureKind isReferenceToNonConstCapture(Sema &S, Expr *E) {
+  assert(E->isLValue() && E->getType().isConstQualified());
+  E = E->IgnoreParens();
+
+  // Must be a reference to a declaration from an enclosing scope.
+  DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E);
+  if (!DRE) return NCCK_None;
+  if (!DRE->refersToEnclosingLocal()) return NCCK_None;
+
+  // The declaration must be a variable which is not declared 'const'.
+  VarDecl *var = dyn_cast<VarDecl>(DRE->getDecl());
+  if (!var) return NCCK_None;
+  if (var->getType().isConstQualified()) return NCCK_None;
+  assert(var->hasLocalStorage() && "capture added 'const' to non-local?");
+
+  // Decide whether the first capture was for a block or a lambda.
+  DeclContext *DC = S.CurContext;
+  while (DC->getParent() != var->getDeclContext())
+    DC = DC->getParent();
+  return (isa<BlockDecl>(DC) ? NCCK_Block : NCCK_Lambda);
+}
+
 /// CheckForModifiableLvalue - Verify that E is a modifiable lvalue.  If not,
 /// emit an error and return true.  If so, return false.
 static bool CheckForModifiableLvalue(Expr *E, SourceLocation Loc, Sema &S) {
@@ -7148,6 +7174,16 @@ static bool CheckForModifiableLvalue(Expr *E, SourceLocation Loc, Sema &S) {
   case Expr::MLV_ConstQualified:
     Diag = diag::err_typecheck_assign_const;
 
+    // Use a specialized diagnostic when we're assigning to an object
+    // from an enclosing function or block.
+    if (NonConstCaptureKind NCCK = isReferenceToNonConstCapture(S, E)) {
+      if (NCCK == NCCK_Block)
+        Diag = diag::err_block_decl_ref_not_modifiable_lvalue;
+      else
+        Diag = diag::err_lambda_decl_ref_not_modifiable_lvalue;
+      break;
+    }
+
     // In ARC, use some specialized diagnostics for occasions where we
     // infer 'const'.  These are always pseudo-strong variables.
     if (S.getLangOpts().ObjCAutoRefCount) {
@@ -7210,9 +7246,6 @@ static bool CheckForModifiableLvalue(Expr *E, SourceLocation Loc, Sema &S) {
   case Expr::MLV_DuplicateVectorComponents:
     Diag = diag::err_typecheck_duplicate_vector_components_not_mlvalue;
     break;
-  case Expr::MLV_NotBlockQualified:
-    Diag = diag::err_block_decl_ref_not_modifiable_lvalue;
-    break;
   case Expr::MLV_ReadonlyProperty:
   case Expr::MLV_NoSetterProperty:
     llvm_unreachable("readonly properties should be processed differently");
index 99fc0caaf7a4f92cc8593bbf6b4e6ff6014c3dfa..80ba0a1d035ba92fe391b181776893cd0fed340c 100644 (file)
@@ -139,3 +139,12 @@ void PR12248()
   unsigned int result = 0;
   auto l = [&]() { ++result; };
 }
+
+namespace ModifyingCapture {
+  void test() {
+    int n = 0;
+    [=] {
+      n = 1; // expected-error {{variable is not assignable (captured by copy)}}
+    };
+  }
+}