From: Douglas Gregor Date: Sun, 12 Feb 2012 18:42:33 +0000 (+0000) Subject: Within the body of a lambda expression, decltype((x)) for an X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f8af98286022f72157d84951b48fde5fb369ab29;p=clang Within the body of a lambda expression, decltype((x)) for an id-expression 'x' will compute the type based on the assumption that 'x' will be captured, even if it isn't captured, per C++11 [expr.prim.lambda]p18. There are two related refactors that go into implementing this: 1) Split out the check that determines whether we should capture a particular variable reference, along with the computation of the type of the field, from the actual act of capturing the variable. 2) Always compute the result of decltype() within Sema, rather than AST, because the decltype() computation is now context-sensitive. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@150347 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index 82570f594d..7f1f7356e5 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -876,7 +876,7 @@ public: QualType getTypeOfType(QualType t) const; /// getDecltypeType - C++0x decltype. - QualType getDecltypeType(Expr *e) const; + QualType getDecltypeType(Expr *e, QualType UnderlyingType) const; /// getUnaryTransformType - unary type transforms QualType getUnaryTransformType(QualType BaseType, QualType UnderlyingType, diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index dcc9fca328..29a07f3cb1 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -3040,10 +3040,6 @@ public: /// DecltypeType (C++0x) class DecltypeType : public Type { Expr *E; - - // FIXME: We could get rid of UnderlyingType if we wanted to: We would have to - // Move getDesugaredType to ASTContext so that it can call getDecltypeForExpr - // from it. QualType UnderlyingType; protected: diff --git a/include/clang/Sema/ScopeInfo.h b/include/clang/Sema/ScopeInfo.h index ea77ff49cb..1861e958ae 100644 --- a/include/clang/Sema/ScopeInfo.h +++ b/include/clang/Sema/ScopeInfo.h @@ -22,11 +22,13 @@ namespace clang { class BlockDecl; +class CXXMethodDecl; class IdentifierInfo; class LabelDecl; class ReturnStmt; class Scope; class SwitchStmt; +class VarDecl; namespace sema { diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index ce36d23706..3333e56e71 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -2282,6 +2282,34 @@ public: void UpdateMarkingForLValueToRValue(Expr *E); void CleanupVarDeclMarking(); + /// \brief Determine whether we can capture the given variable in + /// the given scope. + /// + /// \param Explicit Whether this is an explicit capture (vs. an + /// implicit capture). + /// + /// \param Diagnose Diagnose errors that occur when attempting to perform + /// the capture. + /// + /// \param Var The variable to check for capture. + /// + /// \param Type Will be set to the type used to perform the capture. + /// + /// \param FunctionScopesIndex Will be set to the index of the first + /// scope in which capture will need to be performed. + /// + /// \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); + + /// \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 }; diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index bb93a68d26..cc651f9c9c 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -2913,44 +2913,13 @@ QualType ASTContext::getTypeOfType(QualType tofType) const { return QualType(tot, 0); } -/// getDecltypeForExpr - Given an expr, will return the decltype for that -/// expression, according to the rules in C++0x [dcl.type.simple]p4 -static QualType getDecltypeForExpr(const Expr *e, const ASTContext &Context) { - if (e->isTypeDependent()) - return Context.DependentTy; - - // If e is an id expression or a class member access, decltype(e) is defined - // as the type of the entity named by e. - if (const DeclRefExpr *DRE = dyn_cast(e)) { - if (const ValueDecl *VD = dyn_cast(DRE->getDecl())) - return VD->getType(); - } - if (const MemberExpr *ME = dyn_cast(e)) { - if (const FieldDecl *FD = dyn_cast(ME->getMemberDecl())) - return FD->getType(); - } - // If e is a function call or an invocation of an overloaded operator, - // (parentheses around e are ignored), decltype(e) is defined as the - // return type of that function. - if (const CallExpr *CE = dyn_cast(e->IgnoreParens())) - return CE->getCallReturnType(); - - QualType T = e->getType(); - - // Otherwise, where T is the type of e, if e is an lvalue, decltype(e) is - // defined as T&, otherwise decltype(e) is defined as T. - if (e->isLValue()) - T = Context.getLValueReferenceType(T); - - return T; -} /// getDecltypeType - Unlike many "get" functions, we don't unique /// DecltypeType AST's. The only motivation to unique these nodes would be /// memory savings. Since decltype(t) is fairly uncommon, space shouldn't be /// an issue. This doesn't effect the type checker, since it operates /// on canonical types (which are always unique). -QualType ASTContext::getDecltypeType(Expr *e) const { +QualType ASTContext::getDecltypeType(Expr *e, QualType UnderlyingType) const { DecltypeType *dt; // C++0x [temp.type]p2: @@ -2976,8 +2945,8 @@ QualType ASTContext::getDecltypeType(Expr *e) const { dt = Canon; } } else { - QualType T = getDecltypeForExpr(e, *this); - dt = new (*this, TypeAlignment) DecltypeType(e, T, getCanonicalType(T)); + dt = new (*this, TypeAlignment) DecltypeType(e, UnderlyingType, + getCanonicalType(UnderlyingType)); } Types.push_back(dt); return QualType(dt, 0); diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp index 556f97d858..e15bac0b44 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -1581,7 +1581,11 @@ QualType ASTNodeImporter::VisitDecltypeType(const DecltypeType *T) { if (!ToExpr) return QualType(); - return Importer.getToContext().getDecltypeType(ToExpr); + QualType UnderlyingType = Importer.Import(T->getUnderlyingType()); + if (UnderlyingType.isNull()) + return QualType(); + + return Importer.getToContext().getDecltypeType(ToExpr, UnderlyingType); } QualType ASTNodeImporter::VisitUnaryTransformType(const UnaryTransformType *T) { diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index ce5956ae2f..9bd1074116 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -9566,12 +9566,7 @@ static bool shouldAddConstForScope(CapturingScopeInfo *CSI, VarDecl *VD) { return false; } -/// \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) { - CXXRecordDecl *Lambda = LSI->Lambda; - QualType FieldType; +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 @@ -9579,28 +9574,34 @@ static ExprResult captureInLambda(Sema &S, LambdaScopeInfo *LSI, // unspecified whether additional unnamed non-static data // members are declared in the closure type for entities // captured by reference. - FieldType = S.Context.getLValueReferenceType(Type.getNonReferenceType()); - } 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 = Type->getAs()) { - if (!RefType->getPointeeType()->isFunctionType()) - FieldType = RefType->getPointeeType(); - else - FieldType = Type; - } else { - FieldType = Type; - } + 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()) { + 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) { + CXXRecordDecl *Lambda = LSI->Lambda; + QualType FieldType = S.getLambdaCaptureFieldType(Type, ByRef); + // Build the non-static data member. FieldDecl *Field = FieldDecl::Create(S.Context, Lambda, Loc, Loc, 0, FieldType, @@ -9715,20 +9716,20 @@ static ExprResult captureInLambda(Sema &S, LambdaScopeInfo *LSI, return Result; } -// 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, - TryCaptureKind Kind) { +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; + DeclContext *DC = CurContext; - if (var->getDeclContext() == DC) return; - if (!var->hasLocalStorage()) return; + if (Var->getDeclContext() == DC) return false; + if (!Var->hasLocalStorage()) return false; - // Actually try to capture it. - QualType type = var->getType(); - bool Nested = false; + bool HasBlocksAttr = Var->hasAttr(); - unsigned functionScopesIndex = FunctionScopes.size() - 1; + // Figure out whether we can capture the variable. do { // Only block literals and lambda expressions can capture; other // scopes don't work. @@ -9736,84 +9737,125 @@ void Sema::TryCaptureVar(VarDecl *var, SourceLocation loc, if (isa(DC)) ParentDC = DC->getParent(); else if (isa(DC) && + cast(DC)->getOverloadedOperator() == OO_Call && cast(DC->getParent())->isLambda()) ParentDC = DC->getParent()->getParent(); - else - return diagnoseUncapturableValueReference(*this, loc, var, DC); + else { + if (Diagnose) + diagnoseUncapturableValueReference(*this, Loc, Var, DC); + return false; + } CapturingScopeInfo *CSI = - cast(FunctionScopes[functionScopesIndex]); + cast(FunctionScopes[FunctionScopesIndex]); // Check whether we've already captured it. - if (CSI->CaptureMap.count(var)) { + if (CSI->CaptureMap.count(Var)) { // If we found a capture, any subcaptures are nested Nested = true; - if (shouldAddConstForScope(CSI, var)) - type.addConst(); + if (shouldAddConstForScope(CSI, Var)) + Type.addConst(); break; } - functionScopesIndex--; - DC = ParentDC; - } while (var->getDeclContext() != DC); - - bool hasBlocksAttr = var->hasAttr(); - - for (unsigned i = functionScopesIndex + 1, - e = FunctionScopes.size(); i != e; ++i) { - CapturingScopeInfo *CSI = cast(FunctionScopes[i]); - bool isBlock = isa(CSI); - bool isLambda = isa(CSI); + bool IsBlock = isa(CSI); + bool IsLambda = isa(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; + if (IsLambda && !Var->getDeclName()) { + if (Diagnose) { + Diag(Loc, diag::err_lambda_capture_anonymous_var); + Diag(Var->getLocation(), diag::note_declared_at); + } + return false; } // Prohibit variably-modified types; they're difficult to deal with. - if (type->isVariablyModifiedType()) { - 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; + if (Type->isVariablyModifiedType()) { + if (Diagnose) { + 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 false; } // 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; + if (IsBlock && Type->isArrayType()) { + if (Diagnose) { + Diag(Loc, diag::err_ref_array_type); + Diag(Var->getLocation(), diag::note_previous_decl) + << Var->getDeclName(); + } + return false; } // 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; + if (IsLambda && HasBlocksAttr) { + if (Diagnose) { + Diag(Loc, diag::err_lambda_capture_block) + << Var->getDeclName(); + Diag(Var->getLocation(), diag::note_previous_decl) + << Var->getDeclName(); + } + return false; } + if (CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_None && !Explicit) { + // No capture-default + if (Diagnose) { + Diag(Loc, diag::err_lambda_impcap) << Var->getDeclName(); + Diag(Var->getLocation(), diag::note_previous_decl) + << Var->getDeclName(); + Diag(cast(CSI)->Lambda->getLocStart(), + diag::note_lambda_decl); + } + return false; + } + + FunctionScopesIndex--; + DC = ParentDC; + Explicit = false; + } while (!Var->getDeclContext()->Equals(DC)); + + ++FunctionScopesIndex; + return !Type->isVariablyModifiedType(); +} + +// Check if the variable needs to be captured; if so, try to perform +// the capture. +void Sema::TryCaptureVar(VarDecl *var, SourceLocation loc, + TryCaptureKind Kind) { + 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(); + + for (unsigned i = functionScopesIndex, + e = FunctionScopes.size(); i != e; ++i) { + CapturingScopeInfo *CSI = cast(FunctionScopes[i]); + bool isLambda = isa(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_None) { - // No capture-default - Diag(loc, diag::err_lambda_impcap) << var->getDeclName(); - Diag(var->getLocation(), diag::note_previous_decl) << var->getDeclName(); - Diag(cast(CSI)->Lambda->getLocStart(), - diag::note_lambda_decl); - return; } else if (CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_LambdaByval) { // capture-default '=' byRef = false; diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 7d9a33af11..a235872d8f 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/Sema/ScopeInfo.h" #include "clang/Sema/SemaInternal.h" #include "clang/Sema/Template.h" #include "clang/Basic/OpenCL.h" @@ -4368,12 +4369,98 @@ QualType Sema::BuildTypeofExprType(Expr *E, SourceLocation Loc) { return Context.getTypeOfExprType(E); } +/// getDecltypeForExpr - Given an expr, will return the decltype for +/// that expression, according to the rules in C++11 +/// [dcl.type.simple]p4 and C++11 [expr.lambda.prim]p18. +static QualType getDecltypeForExpr(Sema &S, Expr *E) { + if (E->isTypeDependent()) + return S.Context.DependentTy; + + // If e is an id expression or a class member access, decltype(e) is defined + // as the type of the entity named by e. + if (const DeclRefExpr *DRE = dyn_cast(E)) { + if (const ValueDecl *VD = dyn_cast(DRE->getDecl())) + return VD->getType(); + } + if (const MemberExpr *ME = dyn_cast(E)) { + if (const FieldDecl *FD = dyn_cast(ME->getMemberDecl())) + return FD->getType(); + } + // If e is a function call or an invocation of an overloaded operator, + // (parentheses around e are ignored), decltype(e) is defined as the + // return type of that function. + if (const CallExpr *CE = dyn_cast(E->IgnoreParens())) + return CE->getCallReturnType(); + + // C++11 [expr.lambda.prim]p18: + // Every occurrence of decltype((x)) where x is a possibly + // parenthesized id-expression that names an entity of automatic + // storage duration is treated as if x were transformed into an + // access to a corresponding data member of the closure type that + // would have been declared if x were an odr-use of the denoted + // entity. + using namespace sema; + if (S.getCurLambda()) { + if (isa(E)) { + if (DeclRefExpr *DRE = dyn_cast(E->IgnoreParens())) { + if (VarDecl *Var = dyn_cast(DRE->getDecl())) { + QualType T = Var->getType(); + unsigned FunctionScopesIndex; + bool Nested; + // Determine whether we can capture this variable. + if (S.canCaptureVariable(Var, DRE->getLocation(), + /*Explicit=*/false, /*Diagnose=*/false, + T, FunctionScopesIndex, Nested)) { + // 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. + if (FunctionScopesIndex == S.FunctionScopes.size()) + --FunctionScopesIndex; + for (unsigned I = FunctionScopesIndex, + E = S.FunctionScopes.size(); + I != E; ++I) { + LambdaScopeInfo *LSI + = dyn_cast(S.FunctionScopes[I]); + if (!LSI) + continue; + + bool ByRef = false; + if (LSI->isCaptured(Var)) + ByRef = LSI->getCapture(Var).isReferenceCapture(); + else + ByRef = (LSI->ImpCaptureStyle + == CapturingScopeInfo::ImpCap_LambdaByref); + + T = S.getLambdaCaptureFieldType(T, ByRef); + if (!ByRef && !LSI->Mutable) + T.addConst(); + } + + if (!T->isReferenceType()) + T = S.Context.getLValueReferenceType(T); + return T; + } + } + } + } + } + + QualType T = E->getType(); + + // Otherwise, where T is the type of e, if e is an lvalue, decltype(e) is + // defined as T&, otherwise decltype(e) is defined as T. + if (E->isLValue()) + T = S.Context.getLValueReferenceType(T); + + return T; +} + QualType Sema::BuildDecltypeType(Expr *E, SourceLocation Loc) { ExprResult ER = CheckPlaceholderExpr(E); if (ER.isInvalid()) return QualType(); E = ER.take(); - return Context.getDecltypeType(E); + return Context.getDecltypeType(E, getDecltypeForExpr(*this, E)); } QualType Sema::BuildUnaryTransformType(QualType BaseType, diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index 312f5c718f..75b5d68ce6 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -3928,8 +3928,10 @@ QualType ASTReader::readTypeRecord(unsigned Index) { return Context.getTypeOfType(UnderlyingType); } - case TYPE_DECLTYPE: - return Context.getDecltypeType(ReadExpr(*Loc.F)); + case TYPE_DECLTYPE: { + QualType UnderlyingType = readType(*Loc.F, Record, Idx); + return Context.getDecltypeType(ReadExpr(*Loc.F), UnderlyingType); + } case TYPE_UNARY_TRANSFORM: { QualType BaseType = readType(*Loc.F, Record, Idx); diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index 5364aa83cb..7a0fed720f 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -222,6 +222,7 @@ void ASTTypeWriter::VisitTypeOfType(const TypeOfType *T) { } void ASTTypeWriter::VisitDecltypeType(const DecltypeType *T) { + Writer.AddTypeRef(T->getUnderlyingType(), Record); Writer.AddStmt(T->getUnderlyingExpr()); Code = TYPE_DECLTYPE; } diff --git a/test/CXX/expr/expr.prim/expr.prim.lambda/p12.cpp b/test/CXX/expr/expr.prim/expr.prim.lambda/p12.cpp index fdf6c53633..e7eb5af891 100644 --- a/test/CXX/expr/expr.prim/expr.prim.lambda/p12.cpp +++ b/test/CXX/expr/expr.prim/expr.prim.lambda/p12.cpp @@ -65,11 +65,11 @@ void f1(int i) { // expected-note{{declared here}} void work(int n) { // expected-note{{declared here}} int m = n*n; int j = 40; // expected-note{{declared here}} - auto m3 = [this,m] { // expected-note 2{{lambda expression begins here}} + auto m3 = [this,m] { // expected-note 3{{lambda expression begins here}} auto m4 = [&,j] { // expected-error{{variable 'j' cannot be implicitly captured in a lambda with no capture-default specified}} int x = n; // expected-error{{variable 'n' cannot be implicitly captured in a lambda with no capture-default specified}} x += m; - x += i; // expected-error{{reference to local variable 'i' declared in enclosing function 'f1'}} + x += i; // expected-error{{variable 'i' cannot be implicitly captured in a lambda with no capture-default specified}} x += f; }; }; diff --git a/test/CXX/expr/expr.prim/expr.prim.lambda/p18.cpp b/test/CXX/expr/expr.prim/expr.prim.lambda/p18.cpp new file mode 100644 index 0000000000..561ead1271 --- /dev/null +++ b/test/CXX/expr/expr.prim/expr.prim.lambda/p18.cpp @@ -0,0 +1,41 @@ +// RUN: %clang_cc1 -std=c++11 %s -Wunused -verify + +template +struct is_same { + static const bool value = false; +}; + +template +struct is_same { + static const bool value = true; +}; + +void f3() { + float x, &r = x; + int i; + int &ir = i; + const int &irc = i; + + [=,&irc,&ir] { + static_assert(is_same::value, "should be float"); + static_assert(is_same::value, + "should be const float&"); + static_assert(is_same::value, "should be float&"); + static_assert(is_same::value, + "should be const float&"); + static_assert(is_same::value, "should be int&"); + static_assert(is_same::value, "should be int&"); + static_assert(is_same::value, + "should be const int&"); + static_assert(is_same::value, + "should be const int&"); + }(); + + [=] { + [=] () mutable { + static_assert(is_same::value, "should be float"); + static_assert(is_same::value, + "should be const float&"); + }(); + }(); +} diff --git a/test/SemaCXX/lambda-expressions.cpp b/test/SemaCXX/lambda-expressions.cpp index 1da57c6c72..afbf9a1fff 100644 --- a/test/SemaCXX/lambda-expressions.cpp +++ b/test/SemaCXX/lambda-expressions.cpp @@ -72,7 +72,8 @@ namespace ImplicitCapture { int f[10]; // expected-note {{declared}} [&]() { return f[2]; }; - (void) ^{ return []() { return f[2]; }; }; // expected-error {{cannot refer to declaration with an array type inside block}} + (void) ^{ return []() { return f[2]; }; }; // expected-error {{variable 'f' cannot be implicitly captured in a lambda with no capture-default specified}} \ + // expected-note{{lambda expression begins here}} struct G { G(); G(G&); int a; }; // expected-note 6 {{not viable}} G g;