Introduce basic ASTs for lambda expressions. This covers:
- Capturing variables by-reference and by-copy within a lambda
- The representation of lambda captures
- The creation of the non-static data members in the lambda class
that store the captured variables
- The initialization of the non-static data members from the
captured variables
- Pretty-printing lambda expressions
There are a number of FIXMEs, both explicit and implied, including:
- Creating a field for a capture of 'this'
- Improved diagnostics for initialization failures when capturing
variables by copy
- Dealing with temporaries created during said initialization
- Template instantiation
- AST (de-)serialization
- Binding and returning the lambda expression; turning it into a
proper temporary
- Lots and lots of semantic constraints
- Parameter pack captures
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@149977 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/AST/ExprCXX.h b/include/clang/AST/ExprCXX.h
index 40c5a4f..6161f92 100644
--- a/include/clang/AST/ExprCXX.h
+++ b/include/clang/AST/ExprCXX.h
@@ -14,11 +14,12 @@
#ifndef LLVM_CLANG_AST_EXPRCXX_H
#define LLVM_CLANG_AST_EXPRCXX_H
-#include "clang/Basic/TypeTraits.h"
-#include "clang/Basic/ExpressionTraits.h"
#include "clang/AST/Expr.h"
#include "clang/AST/UnresolvedSet.h"
#include "clang/AST/TemplateBase.h"
+#include "clang/Basic/ExpressionTraits.h"
+#include "clang/Basic/Lambda.h"
+#include "clang/Basic/TypeTraits.h"
namespace clang {
@@ -1007,6 +1008,274 @@
friend class ASTStmtReader;
};
+/// \brief A C++ lambda expression, which produces a function object
+/// (of unspecified type) that can be invoked later.
+///
+/// Example:
+/// \code
+/// void low_pass_filter(std::vector<double> &values, double cutoff) {
+/// values.erase(std::remove_if(values.begin(), values.end(),
+// [=](double value) { return value > cutoff; });
+/// }
+/// \endcode
+///
+/// Lambda expressions can capture local variables, either by copying
+/// the values of those local variables at the time the function
+/// object is constructed (not when it is called!) or by holding a
+/// reference to the local variable. These captures can occur either
+/// implicitly or can be written explicitly between the square
+/// brackets ([...]) that start the lambda expression.
+class LambdaExpr : public Expr {
+ enum {
+ /// \brief Flag used by the Capture class to indicate that the given
+ /// capture was implicit.
+ Capture_Implicit = 0x01,
+
+ /// \brief Flag used by the Capture class to indciate that the
+ /// given capture was by-copy.
+ Capture_ByCopy = 0x02
+ };
+
+ /// \brief The source range that covers the lambda introducer ([...]).
+ SourceRange IntroducerRange;
+
+ /// \brief The number of captures in this lambda.
+ unsigned NumCaptures : 16;
+
+ /// \brief The number of explicit captures in this lambda.
+ unsigned NumExplicitCaptures : 13;
+
+ /// \brief The default capture kind, which is a value of type
+ /// LambdaCaptureDefault.
+ unsigned CaptureDefault : 2;
+
+ /// \brief Whether this lambda had an explicit parameter list vs. an
+ /// implicit (and empty) parameter list.
+ unsigned ExplicitParams : 1;
+
+ /// \brief The location of the closing brace ('}') that completes
+ /// the lambda.
+ ///
+ /// The location of the brace is also available by looking up the
+ /// function call operator in the lambda class. However, it is
+ /// stored here to improve the performance of getSourceRange(), and
+ /// to avoid having to deserialize the function call operator from a
+ /// module file just to determine the source range.
+ SourceLocation ClosingBrace;
+
+ // Note: The Create method allocates storage after the LambdaExpr
+ // object, which contains the captures, followed by the capture
+ // initializers, and finally the body of the lambda. The capture
+ // initializers and lambda body are placed next to each other so
+ // that the children() function can visit all of them easily.
+
+public:
+ /// \brief Describes the capture of either a variable or 'this'.
+ class Capture {
+ llvm::PointerIntPair<VarDecl *, 2> VarAndBits;
+ SourceLocation Loc;
+ SourceLocation EllipsisLoc;
+
+ friend class ASTStmtReader;
+ friend class ASTStmtWriter;
+
+ public:
+ /// \brief Create a new capture.
+ ///
+ /// \param Loc The source location associated with this capture.
+ ///
+ /// \param Kind The kind of capture (this, byref, bycopy).
+ ///
+ /// \param Implicit Whether the capture was implicit or explicit.
+ ///
+ /// \param Var The local variable being captured, or null if capturing this.
+ ///
+ /// \param EllipsisLoc The location of the ellipsis (...) for a
+ /// capture that is a pack expansion, or an invalid source
+ /// location to indicate that this is not a pack expansion.
+ Capture(SourceLocation Loc, bool Implicit,
+ LambdaCaptureKind Kind, VarDecl *Var = 0,
+ SourceLocation EllipsisLoc = SourceLocation());
+
+ /// \brief Determine the kind of capture.
+ LambdaCaptureKind getCaptureKind() const;
+
+ /// \brief Determine whether this capture handles the C++ 'this'
+ /// pointer.
+ bool capturesThis() const { return VarAndBits.getPointer() == 0; }
+
+ /// \brief Determine whether this capture handles a variable.
+ bool capturesVariable() const { return VarAndBits.getPointer() != 0; }
+
+ /// \brief Retrieve the declaration of the local variable being
+ /// captured.
+ ///
+ /// This operation is only valid if this capture does not capture
+ /// 'this'.
+ VarDecl *getCapturedVar() const {
+ assert(!capturesThis() && "No variable available for 'this' capture");
+ return VarAndBits.getPointer();
+ }
+
+ /// \brief Determine whether this was an implicit capture (not
+ /// written between the square brackets introducing the lambda).
+ bool isImplicit() const { return VarAndBits.getInt() & Capture_Implicit; }
+
+ /// \brief Determine whether this was an explicit capture, written
+ /// between the square brackets introducing the lambda.
+ bool isExplicit() const { return !isImplicit(); }
+
+ /// \brief Retrieve the source location of the capture.
+ ///
+ /// For an explicit capture, this returns the location of the
+ /// explicit capture in the source. For an implicit capture, this
+ /// returns the location at which the variable or 'this' was first
+ /// used.
+ SourceLocation getLocation() const { return Loc; }
+
+ /// \brief Determine whether this capture is a pack expansion,
+ /// which captures a function parameter pack.
+ bool isPackExpansion() const { return EllipsisLoc.isValid(); }
+
+ /// \brief Retrieve the location of the ellipsis for a capture
+ /// that is a pack expansion.
+ SourceLocation getEllipsisLoc() const {
+ assert(isPackExpansion() && "No ellipsis location for a non-expansion");
+ return EllipsisLoc;
+ }
+ };
+
+private:
+ /// \brief Construct a lambda expression.
+ LambdaExpr(QualType T, SourceRange IntroducerRange,
+ LambdaCaptureDefault CaptureDefault,
+ ArrayRef<Capture> Captures,
+ bool ExplicitParams,
+ ArrayRef<Expr *> CaptureInits,
+ SourceLocation ClosingBrace);
+
+ Stmt **getStoredStmts() const {
+ LambdaExpr *This = const_cast<LambdaExpr *>(this);
+ return reinterpret_cast<Stmt **>(reinterpret_cast<Capture *>(This + 1)
+ + NumCaptures);
+ }
+
+public:
+ /// \brief Construct a new lambda expression.
+ static LambdaExpr *Create(ASTContext &C,
+ CXXRecordDecl *Class,
+ SourceRange IntroducerRange,
+ LambdaCaptureDefault CaptureDefault,
+ ArrayRef<Capture> Captures,
+ bool ExplicitParams,
+ ArrayRef<Expr *> CaptureInits,
+ SourceLocation ClosingBrace);
+
+ /// \brief Determine the default capture kind for this lambda.
+ LambdaCaptureDefault getCaptureDefault() const {
+ return static_cast<LambdaCaptureDefault>(CaptureDefault);
+ }
+
+ /// \brief An iterator that walks over the captures of the lambda,
+ /// both implicit and explicit.
+ typedef const Capture *capture_iterator;
+
+ /// \brief Retrieve an iterator pointing to the first lambda capture.
+ capture_iterator capture_begin() const {
+ return reinterpret_cast<const Capture *>(this + 1);
+ }
+
+ /// \brief Retrieve an iterator pointing past the end of the
+ /// sequence of lambda captures.
+ capture_iterator capture_end() const {
+ return capture_begin() + NumCaptures;
+ }
+
+ /// \brief Retrieve an iterator pointing to the first explicit
+ /// lambda capture.
+ capture_iterator explicit_capture_begin() const {
+ return capture_begin();
+ }
+
+ /// \brief Retrieve an iterator pointing past the end of the sequence of
+ /// explicit lambda captures.
+ capture_iterator explicit_capture_end() const {
+ return capture_begin() + NumExplicitCaptures;
+ }
+
+ /// \brief Retrieve an iterator pointing to the first implicit
+ /// lambda capture.
+ capture_iterator implicit_capture_begin() const {
+ return explicit_capture_end();
+ }
+
+ /// \brief Retrieve an iterator pointing past the end of the sequence of
+ /// implicit lambda captures.
+ capture_iterator implicit_capture_end() const {
+ return capture_end();
+ }
+
+ /// \brief Iterator that walks over the capture initialization
+ /// arguments.
+ typedef Expr **capture_init_iterator;
+
+ /// \brief Retrieve the first initialization argument for this
+ /// lambda expression (which initializes the first capture field).
+ capture_init_iterator capture_init_begin() const {
+ return reinterpret_cast<Expr **>(getStoredStmts() + 1);
+ }
+
+ /// \brief Retrieve the iterator pointing one past the last
+ /// initialization argument for this lambda expression (which
+ /// initializes the first capture field).
+ capture_init_iterator capture_init_end() const {
+ return capture_init_begin() + NumCaptures;
+ }
+
+ /// \brief Retrieve the source range covering the lambda introducer,
+ /// which contains the explicit capture list surrounded by square
+ /// brackets ([...]).
+ SourceRange getIntroducerRange() const { return IntroducerRange; }
+
+ /// \brief Retrieve the class that corresponds to the lambda, which
+ /// stores the captures in its fields and provides the various
+ /// operations permitted on a lambda (copying, calling).
+ CXXRecordDecl *getLambdaClass() const;
+
+ /// \brief Retrieve the function call operator associated with this
+ /// lambda expression.
+ CXXMethodDecl *getCallOperator() const;
+
+ /// \brief Retrieve the body of the lambda.
+ CompoundStmt *getBody() const {
+ return reinterpret_cast<CompoundStmt *>(getStoredStmts()[NumCaptures]);
+ }
+
+ /// \brief Determine whether the lambda is mutable, meaning that any
+ /// captures values can be modified.
+ bool isMutable() const;
+
+ /// \brief Determine whether this lambda has an explicit parameter
+ /// list vs. an implicit (empty) parameter list.
+ bool hasExplicitParameters() const { return ExplicitParams; }
+
+ static bool classof(const Stmt *T) {
+ return T->getStmtClass() == LambdaExprClass;
+ }
+ static bool classof(const LambdaExpr *) { return true; }
+
+ SourceRange getSourceRange() const {
+ return SourceRange(IntroducerRange.getBegin(), ClosingBrace);
+ }
+
+ child_range children() {
+ return child_range(getStoredStmts(), getStoredStmts() + NumCaptures + 1);
+ }
+
+ friend class ASTStmtReader;
+ friend class ASTStmtWriter;
+};
+
/// CXXScalarValueInitExpr - [C++ 5.2.3p2]
/// Expression "T()" which creates a value-initialized rvalue of type
/// T, which is a non-class type.
diff --git a/include/clang/AST/RecursiveASTVisitor.h b/include/clang/AST/RecursiveASTVisitor.h
index 7aa43ad..dcfe6be 100644
--- a/include/clang/AST/RecursiveASTVisitor.h
+++ b/include/clang/AST/RecursiveASTVisitor.h
@@ -1953,6 +1953,10 @@
TRY_TO(TraverseTypeLoc(S->getTypeSourceInfo()->getTypeLoc()));
})
+DEF_TRAVERSE_STMT(LambdaExpr, {
+ TRY_TO(TraverseStmt(S->getBody()));
+ })
+
DEF_TRAVERSE_STMT(CXXUnresolvedConstructExpr, {
// This is called for code like 'T()', where T is a template argument.
TRY_TO(TraverseTypeLoc(S->getTypeSourceInfo()->getTypeLoc()));
diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h
index 0c0ef46..b989d55 100644
--- a/include/clang/AST/Type.h
+++ b/include/clang/AST/Type.h
@@ -2857,6 +2857,9 @@
bool isSugared() const { return false; }
QualType desugar() const { return QualType(this, 0); }
+ void printExceptionSpecification(std::string &S,
+ PrintingPolicy Policy) const;
+
static bool classof(const Type *T) {
return T->getTypeClass() == FunctionProto;
}
diff --git a/include/clang/Basic/Lambda.h b/include/clang/Basic/Lambda.h
new file mode 100644
index 0000000..df50d94
--- /dev/null
+++ b/include/clang/Basic/Lambda.h
@@ -0,0 +1,38 @@
+//===--- Lambda.h - Types for C++ Lambdas -----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines several types used to describe C++ lambda
+// expressions that are shared between the parser and AST.
+//===----------------------------------------------------------------------===//
+
+
+#ifndef LLVM_CLANG_BASIC_LAMBDA_H
+#define LLVM_CLANG_BASIC_LAMBDA_H
+
+namespace clang {
+
+/// LambdaCaptureDefault - The default, if any, capture method for a
+/// lambda expression.
+enum LambdaCaptureDefault {
+ LCD_None,
+ LCD_ByCopy,
+ LCD_ByRef
+};
+
+/// LambdaCaptureKind - The different capture forms in a lambda
+/// introducer: 'this' or a copied or referenced variable.
+enum LambdaCaptureKind {
+ LCK_This,
+ LCK_ByCopy,
+ LCK_ByRef
+};
+
+} // end namespace clang
+
+#endif // LLVM_CLANG_BASIC_LAMBDA_H
diff --git a/include/clang/Basic/StmtNodes.td b/include/clang/Basic/StmtNodes.td
index 6bb38d0..d728221 100644
--- a/include/clang/Basic/StmtNodes.td
+++ b/include/clang/Basic/StmtNodes.td
@@ -127,6 +127,7 @@
def SubstNonTypeTemplateParmExpr : DStmt<Expr>;
def SubstNonTypeTemplateParmPackExpr : DStmt<Expr>;
def MaterializeTemporaryExpr : DStmt<Expr>;
+def LambdaExpr : DStmt<Expr>;
// Obj-C Expressions.
def ObjCStringLiteral : DStmt<Expr>;
diff --git a/include/clang/Sema/DeclSpec.h b/include/clang/Sema/DeclSpec.h
index c4a96e1..a1c1871 100644
--- a/include/clang/Sema/DeclSpec.h
+++ b/include/clang/Sema/DeclSpec.h
@@ -25,6 +25,7 @@
#include "clang/AST/NestedNameSpecifier.h"
#include "clang/Lex/Token.h"
#include "clang/Basic/ExceptionSpecificationType.h"
+#include "clang/Basic/Lambda.h"
#include "clang/Basic/OperatorKinds.h"
#include "clang/Basic/Specifiers.h"
#include "llvm/ADT/SmallVector.h"
@@ -1917,31 +1918,14 @@
SourceLocation LastLocation;
};
-/// LambdaCaptureDefault - The default, if any, capture method for a
-/// lambda expression.
-enum LambdaCaptureDefault {
- LCD_None,
- LCD_ByCopy,
- LCD_ByRef
-};
-
-/// LambdaCaptureKind - The different capture forms in a lambda
-/// introducer: 'this' or a copied or referenced variable.
-enum LambdaCaptureKind {
- LCK_This,
- LCK_ByCopy,
- LCK_ByRef
-};
-
/// LambdaCapture - An individual capture in a lambda introducer.
struct LambdaCapture {
LambdaCaptureKind Kind;
SourceLocation Loc;
IdentifierInfo* Id;
- LambdaCapture(LambdaCaptureKind Kind,
- SourceLocation Loc,
- IdentifierInfo* Id = 0)
+ LambdaCapture(LambdaCaptureKind Kind, SourceLocation Loc,
+ IdentifierInfo* Id = 0)
: Kind(Kind), Loc(Loc), Id(Id)
{}
};
diff --git a/include/clang/Sema/ScopeInfo.h b/include/clang/Sema/ScopeInfo.h
index a060015..d6b1072 100644
--- a/include/clang/Sema/ScopeInfo.h
+++ b/include/clang/Sema/ScopeInfo.h
@@ -279,12 +279,18 @@
public:
/// \brief The class that describes the lambda.
CXXRecordDecl *Lambda;
-
+
+ /// \brief Source range covering the lambda introducer [...].
+ SourceRange IntroducerRange;
+
/// \brief The number of captures in the \c Captures list that are
/// explicit captures.
unsigned NumExplicitCaptures;
bool Mutable;
+
+ /// \brief Whether the (empty) parameter list is explicit.
+ bool ExplicitParams;
LambdaScopeInfo(DiagnosticsEngine &Diag, CXXRecordDecl *Lambda)
: CapturingScopeInfo(Diag, ImpCap_None), Lambda(Lambda),
diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp
index 8738e5f..f2ebd8c 100644
--- a/lib/AST/Expr.cpp
+++ b/lib/AST/Expr.cpp
@@ -2019,6 +2019,16 @@
return MergeCanThrow(CT, CanSubExprsThrow(C, this));
}
+ case LambdaExprClass: {
+ const LambdaExpr *Lambda = cast<LambdaExpr>(this);
+ CanThrowResult CT = Expr::CT_Cannot;
+ for (LambdaExpr::capture_init_iterator Cap = Lambda->capture_init_begin(),
+ CapEnd = Lambda->capture_init_end();
+ Cap != CapEnd; ++Cap)
+ CT = MergeCanThrow(CT, (*Cap)->CanThrow(C));
+ return CT;
+ }
+
case CXXNewExprClass: {
CanThrowResult CT;
if (isTypeDependent())
diff --git a/lib/AST/ExprCXX.cpp b/lib/AST/ExprCXX.cpp
index df223eb..51dd57c 100644
--- a/lib/AST/ExprCXX.cpp
+++ b/lib/AST/ExprCXX.cpp
@@ -712,6 +712,113 @@
}
}
+LambdaExpr::Capture::Capture(SourceLocation Loc, bool Implicit,
+ LambdaCaptureKind Kind, VarDecl *Var,
+ SourceLocation EllipsisLoc)
+ : VarAndBits(Var, 0), Loc(Loc), EllipsisLoc(EllipsisLoc)
+{
+ unsigned Bits = 0;
+ if (Implicit)
+ Bits |= Capture_Implicit;
+
+ switch (Kind) {
+ case LCK_This:
+ assert(Var == 0 && "'this' capture cannot have a variable!");
+ break;
+
+ case LCK_ByCopy:
+ Bits |= Capture_ByCopy;
+ // Fall through
+ case LCK_ByRef:
+ assert(Var && "capture must have a variable!");
+ break;
+ }
+ VarAndBits.setInt(Bits);
+}
+
+LambdaCaptureKind LambdaExpr::Capture::getCaptureKind() const {
+ if (capturesThis())
+ return LCK_This;
+
+ return (VarAndBits.getInt() & Capture_ByCopy)? LCK_ByCopy : LCK_ByRef;
+}
+
+LambdaExpr::LambdaExpr(QualType T,
+ SourceRange IntroducerRange,
+ LambdaCaptureDefault CaptureDefault,
+ ArrayRef<Capture> Captures,
+ bool ExplicitParams,
+ ArrayRef<Expr *> CaptureInits,
+ SourceLocation ClosingBrace)
+ : Expr(LambdaExprClass, T, VK_RValue, OK_Ordinary,
+ T->isDependentType(), T->isDependentType(), T->isDependentType(),
+ /*ContainsUnexpandedParameterPack=*/false),
+ IntroducerRange(IntroducerRange),
+ NumCaptures(Captures.size()),
+ NumExplicitCaptures(0),
+ CaptureDefault(CaptureDefault),
+ ExplicitParams(ExplicitParams),
+ ClosingBrace(ClosingBrace)
+{
+ assert(CaptureInits.size() == Captures.size() && "Wrong number of arguments");
+
+ // Copy captures.
+ // FIXME: Do we need to update "contains unexpanded parameter pack" here?
+ Capture *ToCapture = reinterpret_cast<Capture *>(this + 1);
+ for (unsigned I = 0, N = Captures.size(); I != N; ++I) {
+ if (Captures[I].isExplicit())
+ ++NumExplicitCaptures;
+ *ToCapture++ = Captures[I];
+ }
+
+ // Copy initialization expressions for the non-static data members.
+ Stmt **Stored = getStoredStmts();
+ for (unsigned I = 0, N = CaptureInits.size(); I != N; ++I)
+ *Stored++ = CaptureInits[I];
+
+ // Copy the body of the lambda.
+ *Stored++ = getCallOperator()->getBody();
+}
+
+LambdaExpr *LambdaExpr::Create(ASTContext &Context,
+ CXXRecordDecl *Class,
+ SourceRange IntroducerRange,
+ LambdaCaptureDefault CaptureDefault,
+ ArrayRef<Capture> Captures,
+ bool ExplicitParams,
+ ArrayRef<Expr *> CaptureInits,
+ SourceLocation ClosingBrace) {
+ // Determine the type of the expression (i.e., the type of the
+ // function object we're creating).
+ QualType T = Context.getTypeDeclType(Class);
+ size_t Size = sizeof(LambdaExpr) + sizeof(Capture) * Captures.size()
+ + sizeof(Stmt *) * (Captures.size() + 1);
+
+ void *Mem = Context.Allocate(Size, llvm::alignOf<LambdaExpr>());
+ return new (Mem) LambdaExpr(T, IntroducerRange, CaptureDefault,
+ Captures, ExplicitParams, CaptureInits,
+ ClosingBrace);
+}
+
+CXXRecordDecl *LambdaExpr::getLambdaClass() const {
+ return getType()->getAsCXXRecordDecl();
+}
+
+CXXMethodDecl *LambdaExpr::getCallOperator() const {
+ CXXRecordDecl *Record = getLambdaClass();
+ DeclarationName Name
+ = Record->getASTContext().DeclarationNames.getCXXOperatorName(OO_Call);
+ DeclContext::lookup_result Calls = Record->lookup(Name);
+ assert(Calls.first != Calls.second && "Missing lambda call operator!");
+ CXXMethodDecl *Result = cast<CXXMethodDecl>(*Calls.first++);
+ assert(Calls.first == Calls.second && "More than lambda one call operator?");
+ return Result;
+}
+
+bool LambdaExpr::isMutable() const {
+ return (getCallOperator()->getTypeQualifiers() & Qualifiers::Const) == 0;
+}
+
ExprWithCleanups::ExprWithCleanups(Expr *subexpr,
ArrayRef<CleanupObject> objects)
: Expr(ExprWithCleanupsClass, subexpr->getType(),
diff --git a/lib/AST/ExprClassification.cpp b/lib/AST/ExprClassification.cpp
index 311b805..79d8dc4 100644
--- a/lib/AST/ExprClassification.cpp
+++ b/lib/AST/ExprClassification.cpp
@@ -330,6 +330,7 @@
// Some C++ expressions are always class temporaries.
case Expr::CXXConstructExprClass:
case Expr::CXXTemporaryObjectExprClass:
+ case Expr::LambdaExprClass:
return Cl::CL_ClassTemporary;
case Expr::VAArgExprClass:
diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp
index bb7042d..283132a 100644
--- a/lib/AST/ExprConstant.cpp
+++ b/lib/AST/ExprConstant.cpp
@@ -6086,6 +6086,7 @@
case Expr::PseudoObjectExprClass:
case Expr::AtomicExprClass:
case Expr::InitListExprClass:
+ case Expr::LambdaExprClass:
return ICEDiag(2, E->getLocStart());
case Expr::SizeOfPackExprClass:
diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp
index 37ae626..e39031d 100644
--- a/lib/AST/ItaniumMangle.cpp
+++ b/lib/AST/ItaniumMangle.cpp
@@ -2247,6 +2247,7 @@
case Expr::ImplicitValueInitExprClass:
case Expr::InitListExprClass:
case Expr::ParenListExprClass:
+ case Expr::LambdaExprClass:
llvm_unreachable("unexpected statement kind");
// FIXME: invent manglings for all these.
diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp
index 7077be1..1e2a494 100644
--- a/lib/AST/StmtPrinter.cpp
+++ b/lib/AST/StmtPrinter.cpp
@@ -1263,6 +1263,98 @@
OS << ")";
}
+void StmtPrinter::VisitLambdaExpr(LambdaExpr *Node) {
+ OS << '[';
+ bool NeedComma = false;
+ switch (Node->getCaptureDefault()) {
+ case LCD_None:
+ break;
+
+ case LCD_ByCopy:
+ OS << '=';
+ NeedComma = true;
+ break;
+
+ case LCD_ByRef:
+ OS << '&';
+ NeedComma = true;
+ break;
+ }
+ for (LambdaExpr::capture_iterator C = Node->explicit_capture_begin(),
+ CEnd = Node->explicit_capture_end();
+ C != CEnd;
+ ++C) {
+ if (NeedComma)
+ OS << ", ";
+ NeedComma = true;
+
+ switch (C->getCaptureKind()) {
+ case LCK_This:
+ OS << "this";
+ break;
+
+ case LCK_ByRef:
+ if (Node->getCaptureDefault() != LCD_ByRef)
+ OS << '&';
+ OS << C->getCapturedVar()->getName();
+ break;
+
+ case LCK_ByCopy:
+ if (Node->getCaptureDefault() != LCD_ByCopy)
+ OS << '=';
+ OS << C->getCapturedVar()->getName();
+ break;
+ }
+ }
+ OS << ']';
+
+ if (Node->hasExplicitParameters()) {
+ OS << " (";
+ CXXMethodDecl *Method = Node->getCallOperator();
+ NeedComma = false;
+ for (CXXMethodDecl::param_iterator P = Method->param_begin(),
+ PEnd = Method->param_end();
+ P != PEnd; ++P) {
+ if (NeedComma) {
+ OS << ", ";
+ } else {
+ NeedComma = true;
+ }
+ std::string ParamStr = (*P)->getNameAsString();
+ (*P)->getOriginalType().getAsStringInternal(ParamStr, Policy);
+ OS << ParamStr;
+ }
+ if (Method->isVariadic()) {
+ if (NeedComma)
+ OS << ", ";
+ OS << "...";
+ }
+ OS << ')';
+
+ if (Node->isMutable())
+ OS << " mutable";
+
+ const FunctionProtoType *Proto
+ = Method->getType()->getAs<FunctionProtoType>();
+ {
+ std::string ExceptionSpec;
+ Proto->printExceptionSpecification(ExceptionSpec, Policy);
+ OS << ExceptionSpec;
+ }
+
+ // FIXME: Attributes
+
+ // FIXME: Suppress trailing return type if it wasn't specified in
+ // the source.
+ OS << " -> " << Proto->getResultType().getAsString(Policy);
+ }
+
+ // Print the body.
+ CompoundStmt *Body = Node->getBody();
+ OS << ' ';
+ PrintStmt(Body);
+}
+
void StmtPrinter::VisitCXXScalarValueInitExpr(CXXScalarValueInitExpr *Node) {
if (TypeSourceInfo *TSInfo = Node->getTypeSourceInfo())
OS << TSInfo->getType().getAsString(Policy) << "()";
diff --git a/lib/AST/StmtProfile.cpp b/lib/AST/StmtProfile.cpp
index 2fb67d9..1d58fd7 100644
--- a/lib/AST/StmtProfile.cpp
+++ b/lib/AST/StmtProfile.cpp
@@ -790,6 +790,24 @@
}
void
+StmtProfiler::VisitLambdaExpr(const LambdaExpr *S) {
+ VisitExpr(S);
+ for (LambdaExpr::capture_iterator C = S->explicit_capture_begin(),
+ CEnd = S->explicit_capture_end();
+ C != CEnd; ++C) {
+ ID.AddInteger(C->getCaptureKind());
+ if (C->capturesVariable()) {
+ VisitDecl(C->getCapturedVar());
+ ID.AddBoolean(C->isPackExpansion());
+ }
+ }
+ // Note: If we actually needed to be able to match lambda
+ // expressions, we would have to consider parameters and return type
+ // here, among other things.
+ VisitStmt(S->getBody());
+}
+
+void
StmtProfiler::VisitCXXScalarValueInitExpr(const CXXScalarValueInitExpr *S) {
VisitExpr(S);
}
diff --git a/lib/AST/TypePrinter.cpp b/lib/AST/TypePrinter.cpp
index b6d7110..7e9e7c5 100644
--- a/lib/AST/TypePrinter.cpp
+++ b/lib/AST/TypePrinter.cpp
@@ -397,6 +397,35 @@
print(T->getElementType(), S);
}
+void
+FunctionProtoType::printExceptionSpecification(std::string &S,
+ PrintingPolicy Policy) const {
+
+ if (hasDynamicExceptionSpec()) {
+ S += " throw(";
+ if (getExceptionSpecType() == EST_MSAny)
+ S += "...";
+ else
+ for (unsigned I = 0, N = getNumExceptions(); I != N; ++I) {
+ if (I)
+ S += ", ";
+
+ S += getExceptionType(I).getAsString(Policy);
+ }
+ S += ")";
+ } else if (isNoexceptExceptionSpec(getExceptionSpecType())) {
+ S += " noexcept";
+ if (getExceptionSpecType() == EST_ComputedNoexcept) {
+ S += "(";
+ llvm::raw_string_ostream EOut(S);
+ getNoexceptExpr()->printPretty(EOut, 0, Policy);
+ EOut.flush();
+ S += EOut.str();
+ S += ")";
+ }
+ }
+}
+
void TypePrinter::printFunctionProto(const FunctionProtoType *T,
std::string &S) {
// If needed for precedence reasons, wrap the inner part in grouping parens.
@@ -470,33 +499,7 @@
S += " &&";
break;
}
-
- if (T->hasDynamicExceptionSpec()) {
- S += " throw(";
- if (T->getExceptionSpecType() == EST_MSAny)
- S += "...";
- else
- for (unsigned I = 0, N = T->getNumExceptions(); I != N; ++I) {
- if (I)
- S += ", ";
-
- std::string ExceptionType;
- print(T->getExceptionType(I), ExceptionType);
- S += ExceptionType;
- }
- S += ")";
- } else if (isNoexceptExceptionSpec(T->getExceptionSpecType())) {
- S += " noexcept";
- if (T->getExceptionSpecType() == EST_ComputedNoexcept) {
- S += "(";
- llvm::raw_string_ostream EOut(S);
- T->getNoexceptExpr()->printPretty(EOut, 0, Policy);
- EOut.flush();
- S += EOut.str();
- S += ")";
- }
- }
-
+ T->printExceptionSpecification(S, Policy);
print(T->getResultType(), S);
}
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index c219797..f656f90 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -9637,11 +9637,77 @@
byRef = hasBlocksAttr || type->isReferenceType();
}
- // Build a copy expression if we are capturing by copy and the copy
- // might be non-trivial.
+ // 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 (!byRef && getLangOptions().CPlusPlus &&
+ if (isLambda) {
+ CXXRecordDecl *Lambda = cast<LambdaScopeInfo>(CSI)->Lambda;
+ QualType FieldType;
+ 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.
+ FieldType = 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;
+ }
+ }
+
+ // Build the non-static data member.
+ FieldDecl *Field
+ = FieldDecl::Create(Context, Lambda, loc, loc, 0, FieldType,
+ Context.getTrivialTypeSourceInfo(FieldType, loc),
+ 0, false, false);
+ Field->setImplicit(true);
+ Field->setAccess(AS_private);
+
+ // C++11 [expr.prim.lambda]p21:
+ // When the lambda-expression is evaluated, the entities that
+ // are captured by copy are used to direct-initialize each
+ // corresponding non-static data member of the resulting closure
+ // object. (For array members, the array elements are
+ // direct-initialized in increasing subscript order.) These
+ // initializations are performed in the (unspecified) order in
+ // which the non-static data members are declared.
+ //
+ // FIXME: Introduce an initialization entity for lambda captures.
+ // FIXME: Totally broken for arrays.
+ Expr *Ref = new (Context) DeclRefExpr(var, type.getNonReferenceType(),
+ VK_LValue, loc);
+ InitializedEntity InitEntity
+ = InitializedEntity::InitializeMember(Field, /*Parent=*/0);
+ InitializationKind InitKind
+ = InitializationKind::CreateDirect(loc, loc, loc);
+ InitializationSequence Init(*this, InitEntity, InitKind, &Ref, 1);
+ if (!Init.Diagnose(*this, InitEntity, InitKind, &Ref, 1)) {
+ ExprResult Result = Init.Perform(*this, InitEntity, InitKind,
+ MultiExprArg(*this, &Ref, 1));
+ 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
@@ -9654,10 +9720,10 @@
// 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.
- // There is no equivalent language in the C++11 specification of lambdas.
- if (isBlock)
- type.addConst();
+ type.addConst();
+ // FIXME: Add an initialized entity for lambda capture.
+ // FIXME: Won't work for arrays, although we do need this behavior.
Expr *declRef = new (Context) DeclRefExpr(var, type, VK_LValue, loc);
ExprResult result =
PerformCopyInitialization(
diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp
index 30b0529..cd2c210 100644
--- a/lib/Sema/SemaExprCXX.cpp
+++ b/lib/Sema/SemaExprCXX.cpp
@@ -4895,6 +4895,7 @@
// at this point, but we need something to attach child declarations to.
QualType MethodTy;
TypeSourceInfo *MethodTyInfo;
+ bool ExplicitParams = true;
if (ParamInfo.getNumTypeObjects() == 0) {
// C++11 [expr.prim.lambda]p4:
// If a lambda-expression does not include a lambda-declarator, it is as
@@ -4904,6 +4905,7 @@
MethodTy = Context.getFunctionType(Context.DependentTy,
/*Args=*/0, /*NumArgs=*/0, EPI);
MethodTyInfo = Context.getTrivialTypeSourceInfo(MethodTy);
+ ExplicitParams = false;
} else {
assert(ParamInfo.isFunctionDeclarator() &&
"lambda-declarator is a function");
@@ -4967,9 +4969,10 @@
LSI->ImpCaptureStyle = LambdaScopeInfo::ImpCap_LambdaByval;
else if (Intro.Default == LCD_ByRef)
LSI->ImpCaptureStyle = LambdaScopeInfo::ImpCap_LambdaByref;
-
+ LSI->IntroducerRange = Intro.Range;
+ LSI->ExplicitParams = ExplicitParams;
LSI->Mutable = (Method->getTypeQualifiers() & Qualifiers::Const) == 0;
-
+
// Handle explicit captures.
for (llvm::SmallVector<LambdaCapture, 4>::const_iterator
C = Intro.Captures.begin(),
@@ -5116,8 +5119,78 @@
ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc,
Stmt *Body, Scope *CurScope) {
- // FIXME: Implement
+ // Leave the expression-evaluation context.
+ DiscardCleanupsInEvaluationContext();
+ PopExpressionEvaluationContext();
+
+ // Leave the context of the lambda.
+ PopDeclContext();
+
+ // FIXME: End-of-lambda checking
+
+ // Collect information from the lambda scope.
+ llvm::SmallVector<LambdaExpr::Capture, 4> Captures;
+ llvm::SmallVector<Expr *, 4> CaptureInits;
+ LambdaCaptureDefault CaptureDefault;
+ CXXRecordDecl *Class;
+ SourceRange IntroducerRange;
+ bool ExplicitParams;
+ {
+ LambdaScopeInfo *LSI = getCurLambda();
+ Class = LSI->Lambda;
+ IntroducerRange = LSI->IntroducerRange;
+ ExplicitParams = LSI->ExplicitParams;
+
+ // Translate captures.
+ for (unsigned I = 0, N = LSI->Captures.size(); I != N; ++I) {
+ LambdaScopeInfo::Capture From = LSI->Captures[I];
+ assert(!From.isBlockCapture() && "Cannot capture __block variables");
+ bool IsImplicit = I >= LSI->NumExplicitCaptures;
+
+ // Handle 'this' capture.
+ if (From.isThisCapture()) {
+ Captures.push_back(LambdaExpr::Capture(From.getLocation(),
+ IsImplicit,
+ LCK_This));
+ CaptureInits.push_back(new (Context) CXXThisExpr(From.getLocation(),
+ getCurrentThisType(),
+ /*isImplicit=*/true));
+ continue;
+ }
+
+ VarDecl *Var = From.getVariable();
+ // FIXME: Handle pack expansions.
+ LambdaCaptureKind Kind = From.isCopyCapture()? LCK_ByCopy : LCK_ByRef;
+ Captures.push_back(LambdaExpr::Capture(From.getLocation(), IsImplicit,
+ Kind, Var));
+ CaptureInits.push_back(From.getCopyExpr());
+ }
+
+ switch (LSI->ImpCaptureStyle) {
+ case CapturingScopeInfo::ImpCap_None:
+ CaptureDefault = LCD_None;
+ break;
+
+ case CapturingScopeInfo::ImpCap_LambdaByval:
+ CaptureDefault = LCD_ByCopy;
+ break;
+
+ case CapturingScopeInfo::ImpCap_LambdaByref:
+ CaptureDefault = LCD_ByRef;
+ break;
+
+ case CapturingScopeInfo::ImpCap_Block:
+ llvm_unreachable("block capture in lambda");
+ break;
+ }
+
+ PopFunctionScopeInfo();
+ }
+
+ Expr *Lambda = LambdaExpr::Create(Context, Class, IntroducerRange,
+ CaptureDefault, Captures, ExplicitParams,
+ CaptureInits, Body->getLocEnd());
+ (void)Lambda;
Diag(StartLoc, diag::err_lambda_unsupported);
- ActOnLambdaError(StartLoc, CurScope);
return ExprError();
}
diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h
index f011b1b..ca72bc7 100644
--- a/lib/Sema/TreeTransform.h
+++ b/lib/Sema/TreeTransform.h
@@ -7620,6 +7620,13 @@
template<typename Derived>
ExprResult
+TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
+ assert(false && "Lambda expressions cannot be instantiated (yet)");
+ return ExprError();
+}
+
+template<typename Derived>
+ExprResult
TreeTransform<Derived>::TransformCXXUnresolvedConstructExpr(
CXXUnresolvedConstructExpr *E) {
TypeSourceInfo *T = getDerived().TransformType(E->getTypeSourceInfo());
diff --git a/lib/Serialization/ASTReaderStmt.cpp b/lib/Serialization/ASTReaderStmt.cpp
index 4292b0f..7857ca8 100644
--- a/lib/Serialization/ASTReaderStmt.cpp
+++ b/lib/Serialization/ASTReaderStmt.cpp
@@ -1048,6 +1048,11 @@
E->Type = GetTypeSourceInfo(Record, Idx);
}
+void ASTStmtReader::VisitLambdaExpr(LambdaExpr *E) {
+ VisitExpr(E);
+ assert(false && "Cannot deserialize lambda expressions yet");
+}
+
void ASTStmtReader::VisitCXXNamedCastExpr(CXXNamedCastExpr *E) {
VisitExplicitCastExpr(E);
SourceRange R = ReadSourceRange(Record, Idx);
diff --git a/lib/Serialization/ASTWriterStmt.cpp b/lib/Serialization/ASTWriterStmt.cpp
index 3cfab77..c71d08e 100644
--- a/lib/Serialization/ASTWriterStmt.cpp
+++ b/lib/Serialization/ASTWriterStmt.cpp
@@ -1022,6 +1022,11 @@
Code = serialization::EXPR_CXX_TEMPORARY_OBJECT;
}
+void ASTStmtWriter::VisitLambdaExpr(LambdaExpr *E) {
+ VisitExpr(E);
+ assert(false && "Cannot serialize lambda expressions yet");
+}
+
void ASTStmtWriter::VisitCXXNamedCastExpr(CXXNamedCastExpr *E) {
VisitExplicitCastExpr(E);
Writer.AddSourceRange(SourceRange(E->getOperatorLoc(), E->getRParenLoc()),
diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 5087ae1..9e5d6a9 100644
--- a/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -486,6 +486,7 @@
case Stmt::SubstNonTypeTemplateParmPackExprClass:
case Stmt::SEHTryStmtClass:
case Stmt::SEHExceptStmtClass:
+ case Stmt::LambdaExprClass:
case Stmt::SEHFinallyStmtClass: {
const ExplodedNode *node = Bldr.generateNode(S, Pred, Pred->getState());
Engine.addAbortedBlock(node, currentBuilderContext->getBlock());
diff --git a/test/CXX/expr/expr.prim/expr.prim.lambda/p14.cpp b/test/CXX/expr/expr.prim/expr.prim.lambda/p14.cpp
new file mode 100644
index 0000000..0bbb9ae
--- /dev/null
+++ b/test/CXX/expr/expr.prim/expr.prim.lambda/p14.cpp
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++11 %s -verify
+
+class NonCopyable {
+ NonCopyable(const NonCopyable&); // expected-note 2 {{implicitly declared private here}}
+};
+
+void capture_by_copy(NonCopyable nc, NonCopyable &ncr) {
+ // FIXME: error messages should talk about capture
+ [nc] { }; // expected-error{{field of type 'NonCopyable' has private copy constructor}} \
+ // expected-error{{lambda expressions are not supported yet}}
+ [ncr] { }; // expected-error{{field of type 'NonCopyable' has private copy constructor}} \
+ // expected-error{{lambda expressions are not supported yet}}
+}
+
+// FIXME: arrays!
diff --git a/test/CXX/expr/expr.prim/expr.prim.lambda/p15.cpp b/test/CXX/expr/expr.prim/expr.prim.lambda/p15.cpp
new file mode 100644
index 0000000..a02340f
--- /dev/null
+++ b/test/CXX/expr/expr.prim/expr.prim.lambda/p15.cpp
@@ -0,0 +1,10 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++11 %s -verify
+
+class NonCopyable {
+ NonCopyable(const NonCopyable&);
+};
+
+void capture_by_ref(NonCopyable nc, NonCopyable &ncr) {
+ [&nc] {}; // expected-error{{lambda expressions are not supported yet}}
+ [&ncr] {}; // expected-error{{lambda expressions are not supported yet}}
+}
diff --git a/tools/libclang/CXCursor.cpp b/tools/libclang/CXCursor.cpp
index fb70d9a..af640bd 100644
--- a/tools/libclang/CXCursor.cpp
+++ b/tools/libclang/CXCursor.cpp
@@ -226,6 +226,7 @@
case Stmt::UnaryExprOrTypeTraitExprClass:
case Stmt::UnaryTypeTraitExprClass:
case Stmt::VAArgExprClass:
+ case Stmt::LambdaExprClass:
K = CXCursor_UnexposedExpr;
break;