diff options
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 14 | ||||
-rw-r--r-- | include/clang/Sema/ScopeInfo.h | 24 | ||||
-rw-r--r-- | include/clang/Sema/Sema.h | 3 | ||||
-rw-r--r-- | lib/Sema/SemaExpr.cpp | 146 | ||||
-rw-r--r-- | lib/Sema/SemaExprCXX.cpp | 3 | ||||
-rw-r--r-- | lib/Sema/SemaExprObjC.cpp | 31 | ||||
-rw-r--r-- | test/CXX/expr/expr.prim/expr.prim.lambda/blocks.cpp | 3 | ||||
-rw-r--r-- | test/SemaCXX/lambda-expressions.cpp | 38 |
8 files changed, 182 insertions, 80 deletions
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 05fc863c61..f0f0a054d3 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -4094,8 +4094,18 @@ def err_capture_non_automatic_variable : Error< 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">; - + "__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< + "variable %0 with variably modified type cannot be captured in " + "a lambda expression">; +def err_lambda_impcap : Error< + "variable %0 cannot be implicitly captured in a lambda with no " + "capture-default specified">; +def note_lambda_decl : Note<"lambda expression begins here">; + + def err_operator_arrow_circular : Error< "circular pointer delegation detected">; def err_pseudo_dtor_base_not_scalar : Error< diff --git a/include/clang/Sema/ScopeInfo.h b/include/clang/Sema/ScopeInfo.h index 5008999e7f..7ee14de2ab 100644 --- a/include/clang/Sema/ScopeInfo.h +++ b/include/clang/Sema/ScopeInfo.h @@ -125,8 +125,15 @@ public: ImplicitCaptureStyle ImpCaptureStyle; class Capture { + // There are two categories of capture: capturing 'this', and capturing + // local variables. There are three ways to capture a local variable: + // capture by copy in the C++11 sense, capture by reference + // in the C++11 sense, and __block capture. Lambdas explicitly specify + // capture by copy or capture by reference. For blocks, __block capture + // applies to variables with that annotation, variables of reference type + // are captured by reference, and other variables are captured by copy. enum CaptureKind { - Cap_This, Cap_ByVal, Cap_ByRef + Cap_This, Cap_ByCopy, Cap_ByRef, Cap_Block }; // The variable being captured (if we are not capturing 'this'), @@ -143,9 +150,9 @@ public: SourceLocation Loc; public: - Capture(VarDecl *Var, bool isByref, bool isNested, SourceLocation Loc, - Expr *Cpy) - : VarAndKind(Var, isByref ? Cap_ByRef : Cap_ByVal), + Capture(VarDecl *Var, bool block, bool byRef, bool isNested, + SourceLocation Loc, Expr *Cpy) + : VarAndKind(Var, block ? Cap_Block : byRef ? Cap_ByRef : Cap_ByCopy), CopyExprAndNested(Cpy, isNested) {} enum IsThisCapture { ThisCapture }; @@ -155,8 +162,9 @@ public: bool isThisCapture() const { return VarAndKind.getInt() == Cap_This; } bool isVariableCapture() const { return !isThisCapture(); } - bool isCopyCapture() const { return VarAndKind.getInt() == Cap_ByVal; } + bool isCopyCapture() const { return VarAndKind.getInt() == Cap_ByCopy; } bool isReferenceCapture() const { return VarAndKind.getInt() == Cap_ByRef; } + bool isBlockCapture() const { return VarAndKind.getInt() == Cap_Block; } bool isNested() { return CopyExprAndNested.getInt(); } VarDecl *getVariable() const { @@ -194,9 +202,9 @@ public: /// or null if unknown. QualType ReturnType; - void AddCapture(VarDecl *Var, bool isByref, bool isNested, SourceLocation Loc, - Expr *Cpy) { - Captures.push_back(Capture(Var, isByref, isNested, Loc, Cpy)); + void AddCapture(VarDecl *Var, bool isBlock, bool isByref, bool isNested, + SourceLocation Loc, Expr *Cpy) { + Captures.push_back(Capture(Var, isBlock, isByref, isNested, Loc, Cpy)); CaptureMap[Var] = Captures.size(); } diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 86a983c1d9..efa57cb8a5 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -2284,6 +2284,7 @@ public: void UpdateMarkingForLValueToRValue(Expr *E); void CleanupVarDeclMarking(); + void TryCaptureVar(VarDecl *var, SourceLocation loc); void MarkDeclarationsReferencedInType(SourceLocation Loc, QualType T); void MarkDeclarationsReferencedInExpr(Expr *E); @@ -5466,7 +5467,7 @@ public: SourceLocation receiverNameLoc, SourceLocation propertyNameLoc); - ObjCMethodDecl *tryCaptureObjCSelf(); + ObjCMethodDecl *tryCaptureObjCSelf(SourceLocation Loc); /// \brief Describes the kind of message expression indicated by a message /// send that starts with an identifier. diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index df17297e47..8e196fbfe2 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -8747,7 +8747,7 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc, CapturingScopeInfo::Capture &Cap = BSI->Captures[i]; if (Cap.isThisCapture()) continue; - BlockDecl::Capture NewCap(Cap.getVariable(), Cap.isReferenceCapture(), + BlockDecl::Capture NewCap(Cap.getVariable(), Cap.isBlockCapture(), Cap.isNested(), Cap.getCopyExpr()); Captures.push_back(NewCap); } @@ -9440,10 +9440,21 @@ diagnoseUncapturableValueReference(Sema &S, SourceLocation loc, << var->getIdentifier(); } -static void tryCaptureVar(Sema &S, VarDecl *var, - SourceLocation loc) { - // Check if the variable needs to be captured. - DeclContext *DC = S.CurContext; +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)) + return !LSI->Mutable; + return false; +} + +// Check if the variable needs to be captured; if so, try to perform +// the capture. +// FIXME: Add support for explicit captures. +void Sema::TryCaptureVar(VarDecl *var, SourceLocation loc) { + DeclContext *DC = CurContext; if (var->getDeclContext() == DC) return; if (!var->hasLocalStorage()) return; @@ -9451,87 +9462,144 @@ static void tryCaptureVar(Sema &S, VarDecl *var, QualType type = var->getType(); bool Nested = false; - unsigned functionScopesIndex = S.FunctionScopes.size() - 1; + unsigned functionScopesIndex = FunctionScopes.size() - 1; do { - // Only blocks (and eventually C++0x closures) can capture; other + // Only block literals and lambda expressions can capture; other // scopes don't work. - // FIXME: Make this function support lambdas! - if (!isa<BlockDecl>(DC)) - return diagnoseUncapturableValueReference(S, loc, var, DC); + DeclContext *ParentDC; + if (isa<BlockDecl>(DC)) + ParentDC = DC->getParent(); + else if (isa<CXXMethodDecl>(DC) && + cast<CXXRecordDecl>(DC->getParent())->isLambda()) + ParentDC = DC->getParent()->getParent(); + else + return diagnoseUncapturableValueReference(*this, loc, var, DC); - BlockScopeInfo *blockScope = - cast<BlockScopeInfo>(S.FunctionScopes[functionScopesIndex]); - assert(blockScope->TheDecl == static_cast<BlockDecl*>(DC)); + CapturingScopeInfo *CSI = + cast<CapturingScopeInfo>(FunctionScopes[functionScopesIndex]); - // Check whether we've already captured it in this block. - if (blockScope->CaptureMap.count(var)) { + // Check whether we've already captured it. + if (CSI->CaptureMap.count(var)) { + // If we found a capture, any subcaptures are nested Nested = true; + + if (shouldAddConstForScope(CSI, var)) + type.addConst(); break; } functionScopesIndex--; - DC = cast<BlockDecl>(DC)->getDeclContext(); + DC = ParentDC; } while (var->getDeclContext() != DC); - bool byRef = var->hasAttr<BlocksAttr>(); + bool hasBlocksAttr = var->hasAttr<BlocksAttr>(); for (unsigned i = functionScopesIndex + 1, - e = S.FunctionScopes.size(); i != e; ++i) { - // Prohibit variably-modified types. + e = FunctionScopes.size(); i != e; ++i) { + CapturingScopeInfo *CSI = cast<CapturingScopeInfo>(FunctionScopes[i]); + bool isBlock = isa<BlockScopeInfo>(CSI); + bool isLambda = isa<LambdaScopeInfo>(CSI); + + // 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()) { + Diag(loc, diag::err_lambda_capture_anonymous_var); + Diag(var->getLocation(), diag::note_declared_at); + return; + } + + // Prohibit variably-modified types; they're difficult to deal with. if (type->isVariablyModifiedType()) { - S.Diag(loc, diag::err_ref_vm_type); - S.Diag(var->getLocation(), diag::note_declared_at); + if (isBlock) + Diag(loc, diag::err_ref_vm_type); + else + Diag(loc, diag::err_lambda_capture_vm_type) << var->getDeclName(); + Diag(var->getLocation(), diag::note_previous_decl) << var->getDeclName(); return; } - // Prohibit arrays, even in __block variables, but not references to - // them. - if (type->isArrayType()) { - S.Diag(loc, diag::err_ref_array_type); - S.Diag(var->getLocation(), diag::note_declared_at); + // Blocks are not allowed to capture arrays. + if (isBlock && type->isArrayType()) { + Diag(loc, diag::err_ref_array_type); + Diag(var->getLocation(), diag::note_previous_decl) << var->getDeclName(); return; } - // Build a copy expression. + // Lambdas are not allowed to capture __block variables; they don't + // support the expected semantics. + if (isLambda && hasBlocksAttr) { + Diag(loc, diag::err_lambda_capture_block) << var->getDeclName(); + Diag(var->getLocation(), diag::note_previous_decl) << var->getDeclName(); + return; + } + + bool byRef; + if (CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_None) { + // No capture-default + 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; + } else if (CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_LambdaByval) { + // capture-default '=' + byRef = false; + } else if (CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_LambdaByref) { + // capture-default '&' + byRef = true; + } 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 and the copy + // might be non-trivial. Expr *copyExpr = 0; const RecordType *rtype; - if (!byRef && S.getLangOptions().CPlusPlus && !type->isDependentType() && - (rtype = type->getAs<RecordType>())) { - + 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)) - S.FinalizeVarWithDestructor(var, rtype); + 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(); + // There is no equivalent language in the C++11 specification of lambdas. + if (isBlock) + type.addConst(); - Expr *declRef = new (S.Context) DeclRefExpr(var, type, VK_LValue, loc); + Expr *declRef = new (Context) DeclRefExpr(var, type, VK_LValue, loc); ExprResult result = - S.PerformCopyInitialization( + PerformCopyInitialization( InitializedEntity::InitializeBlock(var->getLocation(), type, false), - loc, S.Owned(declRef)); + 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 = S.MaybeCreateExprWithCleanups(result); + result = MaybeCreateExprWithCleanups(result); copyExpr = result.take(); } } - BlockScopeInfo *blockScope = cast<BlockScopeInfo>(S.FunctionScopes[i]); - blockScope->AddCapture(var, byRef, Nested, loc, copyExpr); + CSI->AddCapture(var, hasBlocksAttr, byRef, Nested, loc, copyExpr); Nested = true; + if (shouldAddConstForScope(CSI, var)) + type.addConst(); } } @@ -9544,7 +9612,7 @@ static void MarkVarDeclODRUsed(Sema &SemaRef, VarDecl *Var, if (old.isInvalid()) old = Loc; } - tryCaptureVar(SemaRef, Var, Loc); + SemaRef.TryCaptureVar(Var, Loc); Var->setUsed(true); } diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 1ff6f59bae..3dd271bb8a 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -5051,7 +5051,8 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, // the variable. // FIXME: Unify with normal capture path, so we get all of the necessary // nested captures. - LSI->AddCapture(Var, C->Kind == LCK_ByRef, /*isNested=*/false, C->Loc, 0); + LSI->AddCapture(Var, /*isBlock*/false, C->Kind == LCK_ByRef, + /*isNested=*/false, C->Loc, 0); } LSI->finishedExplicitCaptures(); diff --git a/lib/Sema/SemaExprObjC.cpp b/lib/Sema/SemaExprObjC.cpp index 7e2180fe0e..04aa45576e 100644 --- a/lib/Sema/SemaExprObjC.cpp +++ b/lib/Sema/SemaExprObjC.cpp @@ -237,14 +237,8 @@ ExprResult Sema::ParseObjCProtocolExpression(IdentifierInfo *ProtocolId, } /// Try to capture an implicit reference to 'self'. -ObjCMethodDecl *Sema::tryCaptureObjCSelf() { - // Ignore block scopes: we can capture through them. - DeclContext *DC = CurContext; - while (true) { - if (isa<BlockDecl>(DC)) DC = cast<BlockDecl>(DC)->getDeclContext(); - else if (isa<EnumDecl>(DC)) DC = cast<EnumDecl>(DC)->getDeclContext(); - else break; - } +ObjCMethodDecl *Sema::tryCaptureObjCSelf(SourceLocation Loc) { + DeclContext *DC = getFunctionLevelDeclContext(); // If we're not in an ObjC method, error out. Note that, unlike the // C++ case, we don't require an instance method --- class methods @@ -253,22 +247,7 @@ ObjCMethodDecl *Sema::tryCaptureObjCSelf() { if (!method) return 0; - ImplicitParamDecl *self = method->getSelfDecl(); - assert(self && "capturing 'self' in non-definition?"); - - // Mark that we're closing on 'this' in all the block scopes, if applicable. - for (unsigned idx = FunctionScopes.size() - 1; - isa<BlockScopeInfo>(FunctionScopes[idx]); - --idx) { - BlockScopeInfo *blockScope = cast<BlockScopeInfo>(FunctionScopes[idx]); - unsigned &captureIndex = blockScope->CaptureMap[self]; - if (captureIndex) break; - - bool nested = isa<BlockScopeInfo>(FunctionScopes[idx-1]); - blockScope->AddCapture(self, /*byref*/ false, nested, self->getLocation(), - /*copy*/ 0); - captureIndex = blockScope->Captures.size(); // +1 - } + TryCaptureVar(method->getSelfDecl(), Loc); return method; } @@ -763,7 +742,7 @@ ActOnClassPropertyRefExpr(IdentifierInfo &receiverName, if (receiverNamePtr->isStr("super")) { IsSuper = true; - if (ObjCMethodDecl *CurMethod = tryCaptureObjCSelf()) { + if (ObjCMethodDecl *CurMethod = tryCaptureObjCSelf(receiverNameLoc)) { if (CurMethod->isInstanceMethod()) { QualType T = Context.getObjCInterfaceType(CurMethod->getClassInterface()); @@ -975,7 +954,7 @@ ExprResult Sema::ActOnSuperMessage(Scope *S, SourceLocation RBracLoc, MultiExprArg Args) { // Determine whether we are inside a method or not. - ObjCMethodDecl *Method = tryCaptureObjCSelf(); + ObjCMethodDecl *Method = tryCaptureObjCSelf(SuperLoc); if (!Method) { Diag(SuperLoc, diag::err_invalid_receiver_to_message_super); return ExprError(); diff --git a/test/CXX/expr/expr.prim/expr.prim.lambda/blocks.cpp b/test/CXX/expr/expr.prim/expr.prim.lambda/blocks.cpp index faf686d111..2ed738d3fd 100644 --- a/test/CXX/expr/expr.prim/expr.prim.lambda/blocks.cpp +++ b/test/CXX/expr/expr.prim/expr.prim.lambda/blocks.cpp @@ -5,7 +5,6 @@ void block_capture_errors() { (void)[var] { }; // expected-error{{__block variable 'var' cannot be captured in a lambda}} \ // expected-error{{lambda expressions are not supported yet}} - // FIXME: this should produce the same error as above - (void)[=] { var = 17; }; // expected-error{{reference to local variable 'var' declared in enclosed function 'block_capture_errors'}} \ + (void)[=] { var = 17; }; // expected-error{{__block variable 'var' cannot be captured in a lambda}} \ // expected-error{{lambda expressions are not supported yet}} } diff --git a/test/SemaCXX/lambda-expressions.cpp b/test/SemaCXX/lambda-expressions.cpp index a2dc7070dc..0f7d5484a8 100644 --- a/test/SemaCXX/lambda-expressions.cpp +++ b/test/SemaCXX/lambda-expressions.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -std=c++0x -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++0x -fsyntax-only -verify -fblocks %s namespace std { class type_info; }; @@ -42,3 +42,39 @@ namespace ReturnDeduction { [](){ return 1; return 1; }; // expected-error {{not supported yet}} } } + +namespace ImplicitCapture { + void test() { + int a = 0; // expected-note 3 {{declared}} + []() { return a; }; // expected-error {{variable 'a' cannot be implicitly captured in a lambda with no capture-default specified}} expected-note {{begins here}} expected-error {{not supported yet}} + [&]() { return a; }; // expected-error {{not supported yet}} + [=]() { return a; }; // expected-error {{not supported yet}} + [=]() { int* b = &a; }; // expected-error {{cannot initialize a variable of type 'int *' with an rvalue of type 'const int *'}} expected-error {{not supported yet}} + [=]() { return [&]() { return a; }; }; // expected-error 2 {{not supported yet}} + []() { return [&]() { return a; }; }; // expected-error {{variable 'a' cannot be implicitly captured in a lambda with no capture-default specified}} expected-note {{lambda expression begins here}} expected-error 2 {{not supported yet}} + []() { return ^{ return a; }; };// expected-error {{variable 'a' cannot be implicitly captured in a lambda with no capture-default specified}} expected-note {{lambda expression begins here}} expected-error {{not supported yet}} + + const int b = 2; + []() { return b; }; // expected-error {{not supported yet}} + + union { // expected-note {{declared}} + int c; + float d; + }; + d = 3; + [=]() { return c; }; // expected-error {{unnamed variable cannot be implicitly captured in a lambda expression}} expected-error {{not supported yet}} + + __block int e; // expected-note {{declared}} + [&]() { return e; }; // expected-error {{__block variable 'e' cannot be captured in a lambda expression}} expected-error {{not supported yet}} + + int f[10]; // expected-note {{declared}} + [&]() { return f[2]; }; // expected-error {{not supported yet}} + (void) ^{ return []() { return f[2]; }; }; // expected-error {{cannot refer to declaration with an array type inside block}} expected-error {{not supported yet}} + + struct G { G(); G(G&); int a; }; // expected-note 6 {{not viable}} + G g; + [=]() { const G* gg = &g; return gg->a; }; // expected-error {{not supported yet}} + [=]() { return [=]{ const G* gg = &g; return gg->a; }(); }; // expected-error {{no matching constructor for initialization of 'const ImplicitCapture::G'}} expected-error 2 {{not supported yet}} + (void)^{ return [=]{ const G* gg = &g; return gg->a; }(); }; // expected-error 2 {{no matching constructor for initialization of 'const ImplicitCapture::G'}} expected-error {{not supported yet}} + } +} |