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 82570f5..7f1f735 100644
--- a/include/clang/AST/ASTContext.h
+++ b/include/clang/AST/ASTContext.h
@@ -876,7 +876,7 @@
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 dcc9fca..29a07f3 100644
--- a/include/clang/AST/Type.h
+++ b/include/clang/AST/Type.h
@@ -3040,10 +3040,6 @@
/// 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 ea77ff4..1861e95 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 ce36d23..3333e56 100644
--- a/include/clang/Sema/Sema.h
+++ b/include/clang/Sema/Sema.h
@@ -2282,6 +2282,34 @@
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 bb93a68..cc651f9 100644
--- a/lib/AST/ASTContext.cpp
+++ b/lib/AST/ASTContext.cpp
@@ -2913,44 +2913,13 @@
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<DeclRefExpr>(e)) {
- if (const ValueDecl *VD = dyn_cast<ValueDecl>(DRE->getDecl()))
- return VD->getType();
- }
- if (const MemberExpr *ME = dyn_cast<MemberExpr>(e)) {
- if (const FieldDecl *FD = dyn_cast<FieldDecl>(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<CallExpr>(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<Type>" 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 @@
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 556f97d..e15bac0 100644
--- a/lib/AST/ASTImporter.cpp
+++ b/lib/AST/ASTImporter.cpp
@@ -1581,7 +1581,11 @@
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 ce5956a..9bd1074 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -9566,12 +9566,7 @@
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 @@
// 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<ReferenceType>()) {
- 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<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) {
+ 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 @@
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<BlocksAttr>();
- 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 @@
if (isa<BlockDecl>(DC))
ParentDC = DC->getParent();
else if (isa<CXXMethodDecl>(DC) &&
+ cast<CXXMethodDecl>(DC)->getOverloadedOperator() == OO_Call &&
cast<CXXRecordDecl>(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<CapturingScopeInfo>(FunctionScopes[functionScopesIndex]);
+ cast<CapturingScopeInfo>(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<BlocksAttr>();
-
- for (unsigned i = functionScopesIndex + 1,
- e = FunctionScopes.size(); i != e; ++i) {
- CapturingScopeInfo *CSI = cast<CapturingScopeInfo>(FunctionScopes[i]);
- bool isBlock = isa<BlockScopeInfo>(CSI);
- bool isLambda = isa<LambdaScopeInfo>(CSI);
+ 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;
+ 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<LambdaScopeInfo>(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<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_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;
diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp
index 7d9a33a..a235872 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 @@
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<DeclRefExpr>(E)) {
+ if (const ValueDecl *VD = dyn_cast<ValueDecl>(DRE->getDecl()))
+ return VD->getType();
+ }
+ if (const MemberExpr *ME = dyn_cast<MemberExpr>(E)) {
+ if (const FieldDecl *FD = dyn_cast<FieldDecl>(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<CallExpr>(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<ParenExpr>(E)) {
+ if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParens())) {
+ if (VarDecl *Var = dyn_cast<VarDecl>(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<LambdaScopeInfo>(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 312f5c7..75b5d68 100644
--- a/lib/Serialization/ASTReader.cpp
+++ b/lib/Serialization/ASTReader.cpp
@@ -3928,8 +3928,10 @@
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 5364aa8..7a0fed7 100644
--- a/lib/Serialization/ASTWriter.cpp
+++ b/lib/Serialization/ASTWriter.cpp
@@ -222,6 +222,7 @@
}
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 fdf6c53..e7eb5af 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 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 0000000..561ead1
--- /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<typename T, typename U>
+struct is_same {
+ static const bool value = false;
+};
+
+template<typename T>
+struct is_same<T, T> {
+ 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<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(r), float&>::value, "should be float&");
+ static_assert(is_same<decltype(((r))), float const&>::value,
+ "should be const float&");
+ static_assert(is_same<decltype(ir), int&>::value, "should be int&");
+ static_assert(is_same<decltype((ir)), int&>::value, "should be int&");
+ static_assert(is_same<decltype(irc), const int&>::value,
+ "should be const int&");
+ static_assert(is_same<decltype((irc)), const int&>::value,
+ "should be const int&");
+ }();
+
+ [=] {
+ [=] () 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&");
+ }();
+ }();
+}
diff --git a/test/SemaCXX/lambda-expressions.cpp b/test/SemaCXX/lambda-expressions.cpp
index 1da57c6..afbf9a1 100644
--- a/test/SemaCXX/lambda-expressions.cpp
+++ b/test/SemaCXX/lambda-expressions.cpp
@@ -72,7 +72,8 @@
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;