From 01d08018b7cf5ce1601707cfd7a84d22015fc04e Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Tue, 7 Feb 2012 10:09:13 +0000 Subject: [PATCH] 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 --- include/clang/AST/ExprCXX.h | 273 +++++++++++++++++- include/clang/AST/RecursiveASTVisitor.h | 4 + include/clang/AST/Type.h | 3 + include/clang/Basic/Lambda.h | 38 +++ include/clang/Basic/StmtNodes.td | 1 + include/clang/Sema/DeclSpec.h | 22 +- include/clang/Sema/ScopeInfo.h | 8 +- lib/AST/Expr.cpp | 10 + lib/AST/ExprCXX.cpp | 107 +++++++ lib/AST/ExprClassification.cpp | 1 + lib/AST/ExprConstant.cpp | 1 + lib/AST/ItaniumMangle.cpp | 1 + lib/AST/StmtPrinter.cpp | 92 ++++++ lib/AST/StmtProfile.cpp | 18 ++ lib/AST/TypePrinter.cpp | 57 ++-- lib/Sema/SemaExpr.cpp | 78 ++++- lib/Sema/SemaExprCXX.cpp | 81 +++++- lib/Sema/TreeTransform.h | 7 + lib/Serialization/ASTReaderStmt.cpp | 5 + lib/Serialization/ASTWriterStmt.cpp | 5 + lib/StaticAnalyzer/Core/ExprEngine.cpp | 1 + .../expr/expr.prim/expr.prim.lambda/p14.cpp | 15 + .../expr/expr.prim/expr.prim.lambda/p15.cpp | 10 + tools/libclang/CXCursor.cpp | 1 + 24 files changed, 780 insertions(+), 59 deletions(-) create mode 100644 include/clang/Basic/Lambda.h create mode 100644 test/CXX/expr/expr.prim/expr.prim.lambda/p14.cpp create mode 100644 test/CXX/expr/expr.prim/expr.prim.lambda/p15.cpp diff --git a/include/clang/AST/ExprCXX.h b/include/clang/AST/ExprCXX.h index 40c5a4f6a2..6161f92d3f 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 @@ public: 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 &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 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 Captures, + bool ExplicitParams, + ArrayRef CaptureInits, + SourceLocation ClosingBrace); + + Stmt **getStoredStmts() const { + LambdaExpr *This = const_cast(this); + return reinterpret_cast(reinterpret_cast(This + 1) + + NumCaptures); + } + +public: + /// \brief Construct a new lambda expression. + static LambdaExpr *Create(ASTContext &C, + CXXRecordDecl *Class, + SourceRange IntroducerRange, + LambdaCaptureDefault CaptureDefault, + ArrayRef Captures, + bool ExplicitParams, + ArrayRef CaptureInits, + SourceLocation ClosingBrace); + + /// \brief Determine the default capture kind for this lambda. + LambdaCaptureDefault getCaptureDefault() const { + return static_cast(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(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(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(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 7aa43ad544..dcfe6bea98 100644 --- a/include/clang/AST/RecursiveASTVisitor.h +++ b/include/clang/AST/RecursiveASTVisitor.h @@ -1953,6 +1953,10 @@ DEF_TRAVERSE_STMT(CXXTemporaryObjectExpr, { 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 0c0ef46477..b989d55500 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -2857,6 +2857,9 @@ public: 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 0000000000..df50d94894 --- /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 6bb38d0ce7..d728221869 100644 --- a/include/clang/Basic/StmtNodes.td +++ b/include/clang/Basic/StmtNodes.td @@ -127,6 +127,7 @@ def SizeOfPackExpr : DStmt; def SubstNonTypeTemplateParmExpr : DStmt; def SubstNonTypeTemplateParmPackExpr : DStmt; def MaterializeTemporaryExpr : DStmt; +def LambdaExpr : DStmt; // Obj-C Expressions. def ObjCStringLiteral : DStmt; diff --git a/include/clang/Sema/DeclSpec.h b/include/clang/Sema/DeclSpec.h index c4a96e1a3e..a1c1871574 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 @@ private: 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 a060015e5a..d6b10720c6 100644 --- a/include/clang/Sema/ScopeInfo.h +++ b/include/clang/Sema/ScopeInfo.h @@ -279,12 +279,18 @@ class LambdaScopeInfo : public CapturingScopeInfo { 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 8738e5f885..f2ebd8cf48 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -2019,6 +2019,16 @@ Expr::CanThrowResult Expr::CanThrow(ASTContext &C) const { return MergeCanThrow(CT, CanSubExprsThrow(C, this)); } + case LambdaExprClass: { + const LambdaExpr *Lambda = cast(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 df223eb32c..51dd57cfe3 100644 --- a/lib/AST/ExprCXX.cpp +++ b/lib/AST/ExprCXX.cpp @@ -712,6 +712,113 @@ CXXConstructExpr::CXXConstructExpr(ASTContext &C, StmtClass SC, QualType T, } } +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 Captures, + bool ExplicitParams, + ArrayRef 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(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 Captures, + bool ExplicitParams, + ArrayRef 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()); + 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(*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 objects) : Expr(ExprWithCleanupsClass, subexpr->getType(), diff --git a/lib/AST/ExprClassification.cpp b/lib/AST/ExprClassification.cpp index 311b805471..79d8dc48fe 100644 --- a/lib/AST/ExprClassification.cpp +++ b/lib/AST/ExprClassification.cpp @@ -330,6 +330,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { // 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 bb7042d195..283132a356 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -6086,6 +6086,7 @@ static ICEDiag CheckICE(const Expr* E, ASTContext &Ctx) { 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 37ae626683..e39031dced 100644 --- a/lib/AST/ItaniumMangle.cpp +++ b/lib/AST/ItaniumMangle.cpp @@ -2247,6 +2247,7 @@ recurse: 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 7077be1d93..1e2a494daa 100644 --- a/lib/AST/StmtPrinter.cpp +++ b/lib/AST/StmtPrinter.cpp @@ -1263,6 +1263,98 @@ void StmtPrinter::VisitCXXTemporaryObjectExpr(CXXTemporaryObjectExpr *Node) { 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(); + { + 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 2fb67d93a6..1d58fd7b2b 100644 --- a/lib/AST/StmtProfile.cpp +++ b/lib/AST/StmtProfile.cpp @@ -789,6 +789,24 @@ StmtProfiler::VisitCXXTemporaryObjectExpr(const CXXTemporaryObjectExpr *S) { VisitCXXConstructExpr(S); } +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 b6d71108bd..7e9e7c59b1 100644 --- a/lib/AST/TypePrinter.cpp +++ b/lib/AST/TypePrinter.cpp @@ -397,6 +397,35 @@ void TypePrinter::printExtVector(const ExtVectorType *T, std::string &S) { 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 @@ void TypePrinter::printFunctionProto(const FunctionProtoType *T, 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 c21979758d..f656f90ea5 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -9637,11 +9637,77 @@ void Sema::TryCaptureVar(VarDecl *var, SourceLocation loc, 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(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()) { + 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())) { // 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 @@ void Sema::TryCaptureVar(VarDecl *var, SourceLocation loc, // 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 30b0529df9..cd2c210a39 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -4895,6 +4895,7 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, // 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 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, 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 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, 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::const_iterator C = Intro.Captures.begin(), @@ -5116,8 +5119,78 @@ void Sema::ActOnLambdaError(SourceLocation StartLoc, Scope *CurScope) { 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 Captures; + llvm::SmallVector 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 f011b1b8a2..ca72bc7d26 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -7618,6 +7618,13 @@ TreeTransform::TransformCXXTemporaryObjectExpr( E->getLocEnd()); } +template +ExprResult +TreeTransform::TransformLambdaExpr(LambdaExpr *E) { + assert(false && "Lambda expressions cannot be instantiated (yet)"); + return ExprError(); +} + template ExprResult TreeTransform::TransformCXXUnresolvedConstructExpr( diff --git a/lib/Serialization/ASTReaderStmt.cpp b/lib/Serialization/ASTReaderStmt.cpp index 4292b0fd12..7857ca8112 100644 --- a/lib/Serialization/ASTReaderStmt.cpp +++ b/lib/Serialization/ASTReaderStmt.cpp @@ -1048,6 +1048,11 @@ void ASTStmtReader::VisitCXXTemporaryObjectExpr(CXXTemporaryObjectExpr *E) { 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 3cfab776d0..c71d08e29c 100644 --- a/lib/Serialization/ASTWriterStmt.cpp +++ b/lib/Serialization/ASTWriterStmt.cpp @@ -1022,6 +1022,11 @@ void ASTStmtWriter::VisitCXXTemporaryObjectExpr(CXXTemporaryObjectExpr *E) { 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 5087ae1d33..9e5d6a90af 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -486,6 +486,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, 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 0000000000..0bbb9ae744 --- /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 0000000000..a02340f8b9 --- /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 fb70d9a0f6..af640bd7ab 100644 --- a/tools/libclang/CXCursor.cpp +++ b/tools/libclang/CXCursor.cpp @@ -226,6 +226,7 @@ CXCursor cxcursor::MakeCXCursor(Stmt *S, Decl *Parent, CXTranslationUnit TU, case Stmt::UnaryExprOrTypeTraitExprClass: case Stmt::UnaryTypeTraitExprClass: case Stmt::VAArgExprClass: + case Stmt::LambdaExprClass: K = CXCursor_UnexposedExpr; break; -- 2.50.1