From 6afcf8875d4e447645cd7bf3733dd8e2eb8455dc Mon Sep 17 00:00:00 2001 From: "Tareq A. Siraj" Date: Tue, 16 Apr 2013 19:37:38 +0000 Subject: [PATCH] Sema for Captured Statements Add CapturedDecl to be the DeclContext for CapturedStmt, and perform semantic analysis. Currently captures all variables by reference. TODO: templates Author: Ben Langmuir Differential Revision: http://llvm-reviews.chandlerc.com/D433 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@179618 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/Decl.h | 26 +++++ include/clang/AST/DeclBase.h | 1 + include/clang/AST/RecursiveASTVisitor.h | 17 +++- include/clang/AST/Stmt.h | 10 +- include/clang/Basic/DeclNodes.td | 1 + include/clang/Basic/DiagnosticSemaKinds.td | 3 + include/clang/Sema/ScopeInfo.h | 50 +++++++++- include/clang/Sema/Sema.h | 15 +++ include/clang/Serialization/ASTBitCodes.h | 2 + lib/AST/Decl.cpp | 4 + lib/AST/DeclBase.cpp | 2 + lib/AST/Stmt.cpp | 16 +-- lib/AST/StmtPrinter.cpp | 2 +- lib/CodeGen/CGDecl.cpp | 1 + lib/Frontend/PrintPreprocessedOutput.cpp | 10 ++ lib/Parse/ParsePragma.cpp | 17 +++- lib/Sema/ScopeInfo.cpp | 1 + lib/Sema/Sema.cpp | 17 +++- lib/Sema/SemaExpr.cpp | 63 ++++++++++-- lib/Sema/SemaExprCXX.cpp | 13 +++ lib/Sema/SemaLambda.cpp | 1 + lib/Sema/SemaStmt.cpp | 111 +++++++++++++++++++++ lib/Serialization/ASTCommon.cpp | 2 + lib/Serialization/ASTReaderDecl.cpp | 8 ++ lib/Serialization/ASTWriterDecl.cpp | 5 + test/Sema/captured-statements.c | 78 +++++++++++++++ test/SemaCXX/captured-statements.cpp | 52 ++++++++++ tools/libclang/CIndex.cpp | 1 + tools/libclang/RecursiveASTVisitor.h | 17 +++- 29 files changed, 515 insertions(+), 31 deletions(-) create mode 100644 test/Sema/captured-statements.c create mode 100644 test/SemaCXX/captured-statements.cpp diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index 4260ab0bb2..f7286a0388 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -3165,6 +3165,32 @@ public: } }; +/// \brief This represents the body of a CapturedStmt, and serves as its +/// DeclContext. +class CapturedDecl : public Decl, public DeclContext { +private: + Stmt *Body; + + explicit CapturedDecl(DeclContext *DC) + : Decl(Captured, DC, SourceLocation()), DeclContext(Captured) { } + +public: + static CapturedDecl *Create(ASTContext &C, DeclContext *DC); + + Stmt *getBody() const { return Body; } + void setBody(Stmt *B) { Body = B; } + + // Implement isa/cast/dyncast/etc. + static bool classof(const Decl *D) { return classofKind(D->getKind()); } + static bool classofKind(Kind K) { return K == Captured; } + static DeclContext *castToDeclContext(const CapturedDecl *D) { + return static_cast(const_cast(D)); + } + static CapturedDecl *castFromDeclContext(const DeclContext *DC) { + return static_cast(const_cast(DC)); + } +}; + /// \brief Describes a module import declaration, which makes the contents /// of the named module visible in the current translation unit. /// diff --git a/include/clang/AST/DeclBase.h b/include/clang/AST/DeclBase.h index a3e69c0af2..bceb703060 100644 --- a/include/clang/AST/DeclBase.h +++ b/include/clang/AST/DeclBase.h @@ -1046,6 +1046,7 @@ public: bool isFunctionOrMethod() const { switch (DeclKind) { case Decl::Block: + case Decl::Captured: case Decl::ObjCMethod: return true; default: diff --git a/include/clang/AST/RecursiveASTVisitor.h b/include/clang/AST/RecursiveASTVisitor.h index 9b4e481bfd..df41b6fa5a 100644 --- a/include/clang/AST/RecursiveASTVisitor.h +++ b/include/clang/AST/RecursiveASTVisitor.h @@ -1228,8 +1228,9 @@ bool RecursiveASTVisitor::TraverseDeclContextHelper(DeclContext *DC) { for (DeclContext::decl_iterator Child = DC->decls_begin(), ChildEnd = DC->decls_end(); Child != ChildEnd; ++Child) { - // BlockDecls are traversed through BlockExprs. - if (!isa(*Child)) + // BlockDecls and CapturedDecls are traversed through BlockExprs and + // CapturedStmts respectively. + if (!isa(*Child) && !isa(*Child)) TRY_TO(TraverseDecl(*Child)); } @@ -1258,6 +1259,14 @@ DEF_TRAVERSE_DECL(BlockDecl, { return true; }) +DEF_TRAVERSE_DECL(CapturedDecl, { + TRY_TO(TraverseStmt(D->getBody())); + // This return statement makes sure the traversal of nodes in + // decls_begin()/decls_end() (done in the DEF_TRAVERSE_DECL macro) + // is skipped - don't remove it. + return true; + }) + DEF_TRAVERSE_DECL(EmptyDecl, { }) DEF_TRAVERSE_DECL(FileScopeAsmDecl, { @@ -2218,7 +2227,9 @@ DEF_TRAVERSE_STMT(UnresolvedMemberExpr, { DEF_TRAVERSE_STMT(SEHTryStmt, {}) DEF_TRAVERSE_STMT(SEHExceptStmt, {}) DEF_TRAVERSE_STMT(SEHFinallyStmt,{}) -DEF_TRAVERSE_STMT(CapturedStmt, {}) +DEF_TRAVERSE_STMT(CapturedStmt, { + TRY_TO(TraverseDecl(S->getCapturedDecl())); +}) DEF_TRAVERSE_STMT(CXXOperatorCallExpr, { }) DEF_TRAVERSE_STMT(OpaqueValueExpr, { }) diff --git a/include/clang/AST/Stmt.h b/include/clang/AST/Stmt.h index c2cfaa486c..019e678620 100644 --- a/include/clang/AST/Stmt.h +++ b/include/clang/AST/Stmt.h @@ -31,9 +31,9 @@ namespace llvm { namespace clang { class ASTContext; class Attr; + class CapturedDecl; class Decl; class Expr; - class FunctionDecl; class IdentifierInfo; class LabelDecl; class ParmVarDecl; @@ -1959,7 +1959,7 @@ private: unsigned NumCaptures; /// \brief The implicit outlined function. - FunctionDecl *TheFuncDecl; + CapturedDecl *TheCapturedDecl; /// \brief The record for captured variables, a RecordDecl or CXXRecordDecl. RecordDecl *TheRecordDecl; @@ -1967,7 +1967,7 @@ private: /// \brief Construct a captured statement. CapturedStmt(Stmt *S, ArrayRef Captures, ArrayRef CaptureInits, - FunctionDecl *FD, RecordDecl *RD); + CapturedDecl *CD, RecordDecl *RD); /// \brief Construct an empty captured statement. CapturedStmt(EmptyShell Empty, unsigned NumCaptures); @@ -1982,7 +1982,7 @@ public: static CapturedStmt *Create(ASTContext &Context, Stmt *S, ArrayRef Captures, ArrayRef CaptureInits, - FunctionDecl *FD, RecordDecl *RD); + CapturedDecl *CD, RecordDecl *RD); static CapturedStmt *CreateDeserialized(ASTContext &Context, unsigned NumCaptures); @@ -1994,7 +1994,7 @@ public: } /// \brief Retrieve the outlined function declaration. - const FunctionDecl *getCapturedFunctionDecl() const { return TheFuncDecl; } + CapturedDecl *getCapturedDecl() const { return TheCapturedDecl; } /// \brief Retrieve the record declaration for captured variables. const RecordDecl *getCapturedRecordDecl() const { return TheRecordDecl; } diff --git a/include/clang/Basic/DeclNodes.td b/include/clang/Basic/DeclNodes.td index ebcd81252f..ad2afa7a57 100644 --- a/include/clang/Basic/DeclNodes.td +++ b/include/clang/Basic/DeclNodes.td @@ -73,6 +73,7 @@ def Friend : Decl; def FriendTemplate : Decl; def StaticAssert : Decl; def Block : Decl, DeclContext; +def Captured : Decl, DeclContext; def ClassScopeFunctionSpecialization : Decl; def Import : Decl; def OMPThreadPrivate : Decl; diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index cfb1798def..fd9ea51df5 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -4804,6 +4804,9 @@ let CategoryName = "Lambda Issue" in { "here">; } +def err_return_in_captured_stmt : Error< + "cannot return from %0">; + def err_operator_arrow_circular : Error< "circular pointer delegation detected">; def err_pseudo_dtor_base_not_scalar : Error< diff --git a/include/clang/Sema/ScopeInfo.h b/include/clang/Sema/ScopeInfo.h index 2295bf437c..a48422a308 100644 --- a/include/clang/Sema/ScopeInfo.h +++ b/include/clang/Sema/ScopeInfo.h @@ -24,6 +24,7 @@ namespace clang { class Decl; class BlockDecl; +class CapturedDecl; class CXXMethodDecl; class ObjCPropertyDecl; class IdentifierInfo; @@ -73,7 +74,8 @@ protected: enum ScopeKind { SK_Function, SK_Block, - SK_Lambda + SK_Lambda, + SK_CapturedRegion }; public: @@ -319,7 +321,8 @@ public: class CapturingScopeInfo : public FunctionScopeInfo { public: enum ImplicitCaptureStyle { - ImpCap_None, ImpCap_LambdaByval, ImpCap_LambdaByref, ImpCap_Block + ImpCap_None, ImpCap_LambdaByval, ImpCap_LambdaByref, ImpCap_Block, + ImpCap_CapturedRegion }; ImplicitCaptureStyle ImpCaptureStyle; @@ -461,7 +464,8 @@ public: } static bool classof(const FunctionScopeInfo *FSI) { - return FSI->Kind == SK_Block || FSI->Kind == SK_Lambda; + return FSI->Kind == SK_Block || FSI->Kind == SK_Lambda + || FSI->Kind == SK_CapturedRegion; } }; @@ -492,6 +496,46 @@ public: } }; +/// \brief Retains information about a captured region. +class CapturedRegionScopeInfo: public CapturingScopeInfo { +public: + + enum CapturedRegionKind { + CR_Default + }; + + /// \brief The CapturedDecl for this statement. + CapturedDecl *TheCapturedDecl; + /// \brief The captured record type. + RecordDecl *TheRecordDecl; + /// \brief This is the enclosing scope of the captured region. + Scope *TheScope; + /// \brief The kind of captured region. + CapturedRegionKind CapRegionKind; + + CapturedRegionScopeInfo(DiagnosticsEngine &Diag, Scope *S, CapturedDecl *CD, + RecordDecl *RD, CapturedRegionKind K) + : CapturingScopeInfo(Diag, ImpCap_CapturedRegion), + TheCapturedDecl(CD), TheRecordDecl(RD), TheScope(S), CapRegionKind(K) + { + Kind = SK_CapturedRegion; + } + + virtual ~CapturedRegionScopeInfo(); + + /// \brief A descriptive name for the kind of captured region this is. + StringRef getRegionName() const { + switch (CapRegionKind) { + case CR_Default: + return "default captured statement"; + } + } + + static bool classof(const FunctionScopeInfo *FSI) { + return FSI->Kind == SK_CapturedRegion; + } +}; + class LambdaScopeInfo : public CapturingScopeInfo { public: /// \brief The class that describes the lambda. diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 01e646ef4d..f95d6a4321 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -37,6 +37,7 @@ #include "clang/Sema/LocInfoType.h" #include "clang/Sema/ObjCMethodList.h" #include "clang/Sema/Ownership.h" +#include "clang/Sema/ScopeInfo.h" #include "clang/Sema/TypoCorrection.h" #include "clang/Sema/Weak.h" #include "llvm/ADT/ArrayRef.h" @@ -65,6 +66,7 @@ namespace clang { class ArrayType; class AttributeList; class BlockDecl; + class CapturedDecl; class CXXBasePath; class CXXBasePaths; class CXXBindTemporaryExpr; @@ -174,6 +176,7 @@ namespace clang { namespace sema { class AccessedEntity; class BlockScopeInfo; + class CapturedRegionScopeInfo; class CapturingScopeInfo; class CompoundScopeInfo; class DelayedDiagnostic; @@ -916,6 +919,9 @@ public: void PushFunctionScope(); void PushBlockScope(Scope *BlockScope, BlockDecl *Block); void PushLambdaScope(CXXRecordDecl *Lambda, CXXMethodDecl *CallOperator); + void PushCapturedRegionScope(Scope *RegionScope, CapturedDecl *CD, + RecordDecl *RD, + sema::CapturedRegionScopeInfo::CapturedRegionKind K); void PopFunctionScopeInfo(const sema::AnalysisBasedWarnings::Policy *WP =0, const Decl *D = 0, const BlockExpr *blkExpr = 0); @@ -936,6 +942,9 @@ public: /// \brief Retrieve the current lambda expression, if any. sema::LambdaScopeInfo *getCurLambda(); + /// \brief Retrieve the current captured region, if any. + sema::CapturedRegionScopeInfo *getCurCapturedRegion(); + /// WeakTopLevelDeclDecls - access to \#pragma weak-generated Decls SmallVector &WeakTopLevelDecls() { return WeakTopLevelDecl; } @@ -2767,6 +2776,12 @@ public: StmtResult ActOnContinueStmt(SourceLocation ContinueLoc, Scope *CurScope); StmtResult ActOnBreakStmt(SourceLocation BreakLoc, Scope *CurScope); + void ActOnCapturedRegionStart(SourceLocation Loc, Scope *CurScope, + sema::CapturedRegionScopeInfo::CapturedRegionKind Kind); + StmtResult ActOnCapturedRegionEnd(Stmt *S); + void ActOnCapturedRegionError(bool IsInstantiation = false); + RecordDecl *CreateCapturedStmtRecordDecl(CapturedDecl *&CD, + SourceLocation Loc); const VarDecl *getCopyElisionCandidate(QualType ReturnType, Expr *E, bool AllowFunctionParameters); diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h index 04d6a85860..9f5e8b1224 100644 --- a/include/clang/Serialization/ASTBitCodes.h +++ b/include/clang/Serialization/ASTBitCodes.h @@ -963,6 +963,8 @@ namespace clang { DECL_FILE_SCOPE_ASM, /// \brief A BlockDecl record. DECL_BLOCK, + /// \brief A CapturedDecl record. + DECL_CAPTURED, /// \brief A record that stores the set of declarations that are /// lexically stored within a given DeclContext. /// diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 36044826e8..d572335fb3 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -3234,6 +3234,10 @@ MSPropertyDecl *MSPropertyDecl::CreateDeserialized(ASTContext &C, 0, 0); } +CapturedDecl *CapturedDecl::Create(ASTContext &C, DeclContext *DC) { + return new (C) CapturedDecl(DC); +} + EnumConstantDecl *EnumConstantDecl::Create(ASTContext &C, EnumDecl *CD, SourceLocation L, IdentifierInfo *Id, QualType T, diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp index 3f23f3d855..402d83683a 100644 --- a/lib/AST/DeclBase.cpp +++ b/lib/AST/DeclBase.cpp @@ -553,6 +553,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) { case StaticAssert: case ObjCPropertyImpl: case Block: + case Captured: case TranslationUnit: case UsingDirective: @@ -839,6 +840,7 @@ DeclContext *DeclContext::getPrimaryContext() { case Decl::TranslationUnit: case Decl::LinkageSpec: case Decl::Block: + case Decl::Captured: // There is only one DeclContext for these entities. return this; diff --git a/lib/AST/Stmt.cpp b/lib/AST/Stmt.cpp index e120c6a1f8..2a7b170222 100644 --- a/lib/AST/Stmt.cpp +++ b/lib/AST/Stmt.cpp @@ -1038,12 +1038,12 @@ CapturedStmt::Capture *CapturedStmt::getStoredCaptures() const { CapturedStmt::CapturedStmt(Stmt *S, ArrayRef Captures, ArrayRef CaptureInits, - FunctionDecl *FD, + CapturedDecl *CD, RecordDecl *RD) : Stmt(CapturedStmtClass), NumCaptures(Captures.size()), - TheFuncDecl(FD), TheRecordDecl(RD) { + TheCapturedDecl(CD), TheRecordDecl(RD) { assert( S && "null captured statement"); - assert(FD && "null function declaration for captured statement"); + assert(CD && "null captured declaration for captured statement"); assert(RD && "null record declaration for captured statement"); // Copy initialization expressions. @@ -1061,14 +1061,14 @@ CapturedStmt::CapturedStmt(Stmt *S, ArrayRef Captures, CapturedStmt::CapturedStmt(EmptyShell Empty, unsigned NumCaptures) : Stmt(CapturedStmtClass, Empty), NumCaptures(NumCaptures), - TheFuncDecl(0), TheRecordDecl(0) { + TheCapturedDecl(0), TheRecordDecl(0) { getStoredStmts()[NumCaptures] = 0; } CapturedStmt *CapturedStmt::Create(ASTContext &Context, Stmt *S, ArrayRef Captures, ArrayRef CaptureInits, - FunctionDecl *FD, + CapturedDecl *CD, RecordDecl *RD) { // The layout is // @@ -1089,7 +1089,7 @@ CapturedStmt *CapturedStmt::Create(ASTContext &Context, Stmt *S, } void *Mem = Context.Allocate(Size); - return new (Mem) CapturedStmt(S, Captures, CaptureInits, FD, RD); + return new (Mem) CapturedStmt(S, Captures, CaptureInits, CD, RD); } CapturedStmt *CapturedStmt::CreateDeserialized(ASTContext &Context, @@ -1106,8 +1106,8 @@ CapturedStmt *CapturedStmt::CreateDeserialized(ASTContext &Context, } Stmt::child_range CapturedStmt::children() { - // Children are captured field initilizers and the statement being captured. - return child_range(getStoredStmts(), getStoredStmts() + NumCaptures + 1); + // Children are captured field initilizers. + return child_range(getStoredStmts(), getStoredStmts() + NumCaptures); } bool CapturedStmt::capturesVariable(const VarDecl *Var) const { diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp index 469c2846a6..1b2285c794 100644 --- a/lib/AST/StmtPrinter.cpp +++ b/lib/AST/StmtPrinter.cpp @@ -451,7 +451,7 @@ void StmtPrinter::VisitMSAsmStmt(MSAsmStmt *Node) { } void StmtPrinter::VisitCapturedStmt(CapturedStmt *Node) { - PrintStmt(Node->getCapturedStmt()); + PrintStmt(Node->getCapturedDecl()->getBody()); } void StmtPrinter::VisitObjCAtTryStmt(ObjCAtTryStmt *Node) { diff --git a/lib/CodeGen/CGDecl.cpp b/lib/CodeGen/CGDecl.cpp index 15bb6a6e45..173ca6cb09 100644 --- a/lib/CodeGen/CGDecl.cpp +++ b/lib/CodeGen/CGDecl.cpp @@ -70,6 +70,7 @@ void CodeGenFunction::EmitDecl(const Decl &D) { case Decl::Friend: case Decl::FriendTemplate: case Decl::Block: + case Decl::Captured: case Decl::ClassScopeFunctionSpecialization: llvm_unreachable("Declaration should not be in declstmts!"); case Decl::Function: // void X(); diff --git a/lib/Frontend/PrintPreprocessedOutput.cpp b/lib/Frontend/PrintPreprocessedOutput.cpp index 6734d7759b..7e1cae95d7 100644 --- a/lib/Frontend/PrintPreprocessedOutput.cpp +++ b/lib/Frontend/PrintPreprocessedOutput.cpp @@ -137,6 +137,7 @@ public: StringRef RelativePath, const Module *Imported); virtual void Ident(SourceLocation Loc, const std::string &str); + virtual void PragmaCaptured(SourceLocation Loc, StringRef Str); virtual void PragmaComment(SourceLocation Loc, const IdentifierInfo *Kind, const std::string &Str); virtual void PragmaMessage(SourceLocation Loc, StringRef Str); @@ -345,6 +346,15 @@ void PrintPPOutputPPCallbacks::Ident(SourceLocation Loc, const std::string &S) { EmittedTokensOnThisLine = true; } +void PrintPPOutputPPCallbacks::PragmaCaptured(SourceLocation Loc, + StringRef Str) { + startNewLineIfNeeded(); + MoveToLine(Loc); + OS << "#pragma captured"; + + setEmittedDirectiveOnThisLine(); +} + /// MacroDefined - This hook is called whenever a macro definition is seen. void PrintPPOutputPPCallbacks::MacroDefined(const Token &MacroNameTok, const MacroDirective *MD) { diff --git a/lib/Parse/ParsePragma.cpp b/lib/Parse/ParsePragma.cpp index 038636d108..96328e2fb6 100644 --- a/lib/Parse/ParsePragma.cpp +++ b/lib/Parse/ParsePragma.cpp @@ -15,6 +15,7 @@ #include "clang/Lex/Preprocessor.h" #include "clang/Parse/ParseDiagnostic.h" #include "clang/Parse/Parser.h" +#include "clang/Sema/Scope.h" using namespace clang; /// \brief Handle the annotation token produced for #pragma unused(...) @@ -132,7 +133,21 @@ StmtResult Parser::HandlePragmaCaptured() return StmtError(); } - return StmtEmpty(); + SourceLocation Loc = Tok.getLocation(); + + ParseScope CapturedRegionScope(this, Scope::FnScope | Scope::DeclScope); + Actions.ActOnCapturedRegionStart(Loc, getCurScope(), + sema::CapturedRegionScopeInfo::CR_Default); + + StmtResult R = ParseCompoundStatement(); + CapturedRegionScope.Exit(); + + if (R.isInvalid()) { + Actions.ActOnCapturedRegionError(); + return StmtError(); + } + + return Actions.ActOnCapturedRegionEnd(R.get()); } namespace { diff --git a/lib/Sema/ScopeInfo.cpp b/lib/Sema/ScopeInfo.cpp index 4d29a34a73..2f48bec123 100644 --- a/lib/Sema/ScopeInfo.cpp +++ b/lib/Sema/ScopeInfo.cpp @@ -187,3 +187,4 @@ void FunctionScopeInfo::markSafeWeakUse(const Expr *E) { FunctionScopeInfo::~FunctionScopeInfo() { } BlockScopeInfo::~BlockScopeInfo() { } LambdaScopeInfo::~LambdaScopeInfo() { } +CapturedRegionScopeInfo::~CapturedRegionScopeInfo() { } diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp index 6425f34ff5..203b689aa1 100644 --- a/lib/Sema/Sema.cpp +++ b/lib/Sema/Sema.cpp @@ -802,7 +802,7 @@ DeclContext *Sema::getFunctionLevelDeclContext() { DeclContext *DC = CurContext; while (true) { - if (isa(DC) || isa(DC)) { + if (isa(DC) || isa(DC) || isa(DC)) { DC = DC->getParent(); } else if (isa(DC) && cast(DC)->getOverloadedOperator() == OO_Call && @@ -1314,3 +1314,18 @@ IdentifierInfo *Sema::getSuperIdentifier() const { Ident_super = &Context.Idents.get("super"); return Ident_super; } + +void Sema::PushCapturedRegionScope(Scope *S, CapturedDecl *CD, RecordDecl *RD, + CapturedRegionScopeInfo::CapturedRegionKind K) { + CapturingScopeInfo *CSI = new CapturedRegionScopeInfo(getDiagnostics(), + S, CD, RD, K); + CSI->ReturnType = Context.VoidTy; + FunctionScopes.push_back(CSI); +} + +CapturedRegionScopeInfo *Sema::getCurCapturedRegion() { + if (FunctionScopes.empty()) + return 0; + + return dyn_cast(FunctionScopes.back()); +} diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index e96c5d40a3..cdfdc09e06 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -10851,6 +10851,34 @@ diagnoseUncapturableValueReference(Sema &S, SourceLocation loc, // capture. } +/// \brief Capture the given variable in the captured region. +static ExprResult captureInCapturedRegion(Sema &S, CapturedRegionScopeInfo *RSI, + VarDecl *Var, QualType FieldType, + QualType DeclRefType, + SourceLocation Loc, + bool RefersToEnclosingLocal) { + // The current implemention assumes that all variables are captured + // by references. Since there is no capture by copy, no expression evaluation + // will be needed. + // + RecordDecl *RD = RSI->TheRecordDecl; + + FieldDecl *Field + = FieldDecl::Create(S.Context, RD, Loc, Loc, 0, FieldType, + S.Context.getTrivialTypeSourceInfo(FieldType, Loc), + 0, false, ICIS_NoInit); + Field->setImplicit(true); + Field->setAccess(AS_private); + RD->addDecl(Field); + + Expr *Ref = new (S.Context) DeclRefExpr(Var, RefersToEnclosingLocal, + DeclRefType, VK_LValue, Loc); + Var->setReferenced(true); + Var->setUsed(true); + + return Ref; +} + /// \brief Capture the given variable in the given lambda expression. static ExprResult captureInLambda(Sema &S, LambdaScopeInfo *LSI, VarDecl *Var, QualType FieldType, @@ -10991,10 +11019,10 @@ bool Sema::tryCaptureVariable(VarDecl *Var, SourceLocation Loc, bool Explicit = (Kind != TryCapture_Implicit); unsigned FunctionScopesIndex = FunctionScopes.size() - 1; do { - // Only block literals and lambda expressions can capture; other - // scopes don't work. + // Only block literals, captured statements, and lambda expressions can + // capture; other scopes don't work. DeclContext *ParentDC; - if (isa(DC)) + if (isa(DC) || isa(DC)) ParentDC = DC->getParent(); else if (isa(DC) && cast(DC)->getOverloadedOperator() == OO_Call && @@ -11028,7 +11056,7 @@ bool Sema::tryCaptureVariable(VarDecl *Var, SourceLocation Loc, } bool IsBlock = isa(CSI); - bool IsLambda = !IsBlock; + bool IsLambda = isa(CSI); // Lambdas are not allowed to capture unnamed variables // (e.g. anonymous unions). @@ -11188,8 +11216,31 @@ bool Sema::tryCaptureVariable(VarDecl *Var, SourceLocation Loc, SourceLocation(), CaptureType, CopyExpr); Nested = true; continue; - } - + } + + if (CapturedRegionScopeInfo *RSI = dyn_cast(CSI)) { + // By default, capture variables by reference. + bool ByRef = true; + // Using an LValue reference type is consistent with Lambdas (see below). + CaptureType = Context.getLValueReferenceType(DeclRefType); + + Expr *CopyExpr = 0; + if (BuildAndDiagnose) { + ExprResult Result = captureInCapturedRegion(*this, RSI, Var, + CaptureType, DeclRefType, + Loc, Nested); + if (!Result.isInvalid()) + CopyExpr = Result.take(); + } + + // Actually capture the variable. + if (BuildAndDiagnose) + CSI->addCapture(Var, /*isBlock*/false, ByRef, Nested, Loc, + SourceLocation(), CaptureType, CopyExpr); + Nested = true; + continue; + } + LambdaScopeInfo *LSI = cast(CSI); // Determine whether we are capturing by reference or by value. diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index cfc00502c4..89c1bc51c3 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -746,6 +746,7 @@ void Sema::CheckCXXThisCapture(SourceLocation Loc, bool Explicit) { if (CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_LambdaByref || CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_LambdaByval || CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_Block || + CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_CapturedRegion || Explicit) { // This closure can capture 'this'; continue looking upwards. NumClosures++; @@ -778,7 +779,19 @@ void Sema::CheckCXXThisCapture(SourceLocation Loc, bool Explicit) { Field->setAccess(AS_private); Lambda->addDecl(Field); ThisExpr = new (Context) CXXThisExpr(Loc, ThisTy, /*isImplicit=*/true); + } else if (CapturedRegionScopeInfo *RSI = + dyn_cast(FunctionScopes[idx])) { + RecordDecl *RD = RSI->TheRecordDecl; + FieldDecl *Field + = FieldDecl::Create(Context, RD, Loc, Loc, 0, ThisTy, + Context.getTrivialTypeSourceInfo(ThisTy, Loc), + 0, false, ICIS_NoInit); + Field->setImplicit(true); + Field->setAccess(AS_private); + RD->addDecl(Field); + ThisExpr = new (Context) CXXThisExpr(Loc, ThisTy, /*isImplicit*/true); } + bool isNested = NumClosures > 1; CSI->addThisCapture(isNested, Loc, ThisTy, ThisExpr); } diff --git a/lib/Sema/SemaLambda.cpp b/lib/Sema/SemaLambda.cpp index 53fa6dafdd..77e6bfd086 100644 --- a/lib/Sema/SemaLambda.cpp +++ b/lib/Sema/SemaLambda.cpp @@ -862,6 +862,7 @@ ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body, CaptureDefault = LCD_ByCopy; break; + case CapturingScopeInfo::ImpCap_CapturedRegion: case CapturingScopeInfo::ImpCap_LambdaByref: CaptureDefault = LCD_ByRef; break; diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index b3558a548b..88237b08b2 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/lib/Sema/SemaStmt.cpp @@ -2403,6 +2403,10 @@ Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { Diag(ReturnLoc, diag::err_noreturn_block_has_return_expr); return StmtError(); } + } else if (CapturedRegionScopeInfo *CurRegion = + dyn_cast(CurCap)) { + Diag(ReturnLoc, diag::err_return_in_captured_stmt) << CurRegion->getRegionName(); + return StmtError(); } else { LambdaScopeInfo *LSI = cast(CurCap); if (LSI->CallOperator->getType()->getAs()->getNoReturnAttr()){ @@ -2914,3 +2918,110 @@ StmtResult Sema::ActOnMSDependentExistsStmt(SourceLocation KeywordLoc, GetNameFromUnqualifiedId(Name), Nested); } + +RecordDecl* +Sema::CreateCapturedStmtRecordDecl(CapturedDecl *&CD, SourceLocation Loc) +{ + DeclContext *DC = CurContext; + while (!(DC->isFunctionOrMethod() || DC->isRecord() || DC->isFileContext())) + DC = DC->getParent(); + + RecordDecl *RD = 0; + if (getLangOpts().CPlusPlus) + RD = CXXRecordDecl::Create(Context, TTK_Struct, DC, Loc, Loc, /*Id=*/0); + else + RD = RecordDecl::Create(Context, TTK_Struct, DC, Loc, Loc, /*Id=*/0); + + DC->addDecl(RD); + RD->setImplicit(); + RD->startDefinition(); + + CD = CapturedDecl::Create(Context, CurContext); + DC->addDecl(CD); + + return RD; +} + +static void buildCapturedStmtCaptureList( + SmallVectorImpl &Captures, + SmallVectorImpl &CaptureInits, + ArrayRef Candidates) { + + typedef ArrayRef::const_iterator CaptureIter; + for (CaptureIter Cap = Candidates.begin(); Cap != Candidates.end(); ++Cap) { + + if (Cap->isThisCapture()) { + Captures.push_back(CapturedStmt::Capture(Cap->getLocation(), + CapturedStmt::VCK_This)); + CaptureInits.push_back(Cap->getCopyExpr()); + continue; + } + + assert(Cap->isReferenceCapture() && + "non-reference capture not yet implemented"); + + Captures.push_back(CapturedStmt::Capture(Cap->getLocation(), + CapturedStmt::VCK_ByRef, + Cap->getVariable())); + CaptureInits.push_back(Cap->getCopyExpr()); + } +} + +void Sema::ActOnCapturedRegionStart(SourceLocation Loc, Scope *CurScope, + CapturedRegionScopeInfo::CapturedRegionKind Kind) { + CapturedDecl *CD = 0; + RecordDecl *RD = CreateCapturedStmtRecordDecl(CD, Loc); + + // Enter the capturing scope for this captured region. + PushCapturedRegionScope(CurScope, CD, RD, Kind); + + if (CurScope) + PushDeclContext(CurScope, CD); + else + CurContext = CD; + + PushExpressionEvaluationContext(PotentiallyEvaluated); +} + +void Sema::ActOnCapturedRegionError(bool IsInstantiation) { + DiscardCleanupsInEvaluationContext(); + PopExpressionEvaluationContext(); + + if (!IsInstantiation) + PopDeclContext(); + + CapturedRegionScopeInfo *RSI = getCurCapturedRegion(); + RecordDecl *Record = RSI->TheRecordDecl; + Record->setInvalidDecl(); + + SmallVector Fields; + for (RecordDecl::field_iterator I = Record->field_begin(), + E = Record->field_end(); I != E; ++I) + Fields.push_back(*I); + ActOnFields(/*Scope=*/0, Record->getLocation(), Record, Fields, + SourceLocation(), SourceLocation(), /*AttributeList=*/0); + + PopFunctionScopeInfo(); +} + +StmtResult Sema::ActOnCapturedRegionEnd(Stmt *S) { + CapturedRegionScopeInfo *RSI = getCurCapturedRegion(); + + SmallVector Captures; + SmallVector CaptureInits; + buildCapturedStmtCaptureList(Captures, CaptureInits, RSI->Captures); + + CapturedDecl *CD = RSI->TheCapturedDecl; + RecordDecl *RD = RSI->TheRecordDecl; + + CapturedStmt *Res = CapturedStmt::Create(getASTContext(), S, Captures, + CaptureInits, CD, RD); + + CD->setBody(Res->getCapturedStmt()); + RD->completeDefinition(); + + PopDeclContext(); + PopFunctionScopeInfo(); + + return Owned(Res); +} diff --git a/lib/Serialization/ASTCommon.cpp b/lib/Serialization/ASTCommon.cpp index e8cc1553aa..24b268f36d 100644 --- a/lib/Serialization/ASTCommon.cpp +++ b/lib/Serialization/ASTCommon.cpp @@ -119,6 +119,7 @@ serialization::getDefinitiveDeclContext(const DeclContext *DC) { case Decl::CXXConversion: case Decl::ObjCMethod: case Decl::Block: + case Decl::Captured: // Objective C categories, category implementations, and class // implementations can only be defined in one place. case Decl::ObjCCategory: @@ -203,6 +204,7 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) { case Decl::FriendTemplate: case Decl::StaticAssert: case Decl::Block: + case Decl::Captured: case Decl::ClassScopeFunctionSpecialization: case Decl::Import: case Decl::OMPThreadPrivate: diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index 02295d09be..949f593434 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -266,6 +266,7 @@ namespace clang { void VisitFriendTemplateDecl(FriendTemplateDecl *D); void VisitStaticAssertDecl(StaticAssertDecl *D); void VisitBlockDecl(BlockDecl *BD); + void VisitCapturedDecl(CapturedDecl *CD); void VisitEmptyDecl(EmptyDecl *D); std::pair VisitDeclContext(DeclContext *DC); @@ -994,6 +995,10 @@ void ASTDeclReader::VisitBlockDecl(BlockDecl *BD) { captures.end(), capturesCXXThis); } +void ASTDeclReader::VisitCapturedDecl(CapturedDecl *) { + llvm_unreachable("not implemented yet"); +} + void ASTDeclReader::VisitLinkageSpecDecl(LinkageSpecDecl *D) { VisitDecl(D); D->setLanguage((LinkageSpecDecl::LanguageIDs)Record[Idx++]); @@ -2146,6 +2151,9 @@ Decl *ASTReader::ReadDeclRecord(DeclID ID) { case DECL_MS_PROPERTY: D = MSPropertyDecl::CreateDeserialized(Context, ID); break; + case DECL_CAPTURED: + llvm_unreachable("not implemented yet"); + break; case DECL_CXX_BASE_SPECIFIERS: Error("attempt to read a C++ base-specifier record as a declaration"); return 0; diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp index 7d21c584aa..b7ca623d7d 100644 --- a/lib/Serialization/ASTWriterDecl.cpp +++ b/lib/Serialization/ASTWriterDecl.cpp @@ -103,6 +103,7 @@ namespace clang { void VisitFriendTemplateDecl(FriendTemplateDecl *D); void VisitStaticAssertDecl(StaticAssertDecl *D); void VisitBlockDecl(BlockDecl *D); + void VisitCapturedDecl(CapturedDecl *D); void VisitEmptyDecl(EmptyDecl *D); void VisitDeclContext(DeclContext *DC, uint64_t LexicalOffset, @@ -824,6 +825,10 @@ void ASTDeclWriter::VisitBlockDecl(BlockDecl *D) { Code = serialization::DECL_BLOCK; } +void ASTDeclWriter::VisitCapturedDecl(CapturedDecl *) { + llvm_unreachable("not implemented yet"); +} + void ASTDeclWriter::VisitLinkageSpecDecl(LinkageSpecDecl *D) { VisitDecl(D); Record.push_back(D->getLanguage()); diff --git a/test/Sema/captured-statements.c b/test/Sema/captured-statements.c new file mode 100644 index 0000000000..9285a7802d --- /dev/null +++ b/test/Sema/captured-statements.c @@ -0,0 +1,78 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s -fblocks + +void test_gotos() { + goto L1; // expected-error {{use of undeclared label 'L1'}} + goto L3; // OK + #pragma clang __debug captured + { +L1: + goto L2; // OK +L2: + goto L3; // expected-error {{use of undeclared label 'L3'}} + } +L3: ; +} + +void test_break_continue() { + while (1) { + #pragma clang __debug captured + { + break; // expected-error {{'break' statement not in loop or switch statement}} + continue; // expected-error {{'continue' statement not in loop statement}} + } + } +} + +void test_return() { + while (1) { + #pragma clang __debug captured + { + return; // expected-error {{cannot return from default captured statement}} + } + } +} + +void test_nest() { + int x; + #pragma clang __debug captured + { + int y; + #pragma clang __debug captured + { + int z; + #pragma clang __debug captured + { + x = z = y; // OK + } + } + } +} + +void test_nest_block() { + __block int x; + int y; + ^{ + int z; + #pragma clang __debug captured + { + x = y; // OK + y = z; // expected-error{{variable is not assignable (missing __block type specifier)}} + z = y; // OK + } + }(); + + __block int a; + int b; + #pragma clang __debug captured + { + __block int c; + int d; + ^{ + a = b; // OK + a = c; // OK + b = d; // OK - Consistent with block inside a lambda + c = a; // OK + d = b; // expected-error{{variable is not assignable (missing __block type specifier)}} + }(); + } +} diff --git a/test/SemaCXX/captured-statements.cpp b/test/SemaCXX/captured-statements.cpp new file mode 100644 index 0000000000..15879a1ebc --- /dev/null +++ b/test/SemaCXX/captured-statements.cpp @@ -0,0 +1,52 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s -fblocks + +void test_nest_lambda() { + int x; + int y; + [&,y]() { + int z; + #pragma clang __debug captured + { + x = y; // OK + y = z; // expected-error{{cannot assign to a variable captured by copy in a non-mutable lambda}} + z = y; // OK + } + }(); + + int a; + #pragma clang __debug captured + { + int b; + int c; + [&,c]() { + a = b; // OK + b = c; // OK + c = a; // expected-error{{cannot assign to a variable captured by copy in a non-mutable lambda}} + }(); + } +} + +class test_obj_capture { + int a; + void b(); + static void test() { + test_obj_capture c; + #pragma clang __debug captured + { (void)c.a; } // OK + #pragma clang __debug captured + { c.b(); } // OK + } +}; + +class test_this_capture { + int a; + void b(); + void test() { + #pragma clang __debug captured + { (void)this; } // OK + #pragma clang __debug captured + { (void)a; } // OK + #pragma clang __debug captured + { b(); } // OK + } +}; diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index d1aeee04ea..95b49fcd25 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -4474,6 +4474,7 @@ CXCursor clang_getCursorDefinition(CXCursor C) { case Decl::FileScopeAsm: case Decl::StaticAssert: case Decl::Block: + case Decl::Captured: case Decl::Label: // FIXME: Is this right?? case Decl::ClassScopeFunctionSpecialization: case Decl::Import: diff --git a/tools/libclang/RecursiveASTVisitor.h b/tools/libclang/RecursiveASTVisitor.h index 592f168725..6c9da93b73 100644 --- a/tools/libclang/RecursiveASTVisitor.h +++ b/tools/libclang/RecursiveASTVisitor.h @@ -1170,8 +1170,9 @@ bool RecursiveASTVisitor::TraverseDeclContextHelper(DeclContext *DC) { for (DeclContext::decl_iterator Child = DC->decls_begin(), ChildEnd = DC->decls_end(); Child != ChildEnd; ++Child) { - // BlockDecls are traversed through BlockExprs. - if (!isa(*Child)) + // BlockDecls and CapturedDecls are traversed through BlockExprs and + // CapturedStmts respectively. + if (!isa(*Child) && !isa(*Child)) TRY_TO(TraverseDecl(*Child)); } @@ -1200,6 +1201,14 @@ DEF_TRAVERSE_DECL(BlockDecl, { return true; }) +DEF_TRAVERSE_DECL(CapturedDecl, { + TRY_TO(TraverseStmt(D->getBody())); + // This return statement makes sure the traversal of nodes in + // decls_begin()/decls_end() (done in the DEF_TRAVERSE_DECL macro) + // is skipped - don't remove it. + return true; + }) + DEF_TRAVERSE_DECL(EmptyDecl, { }) DEF_TRAVERSE_DECL(FileScopeAsmDecl, { @@ -1839,7 +1848,6 @@ DEF_TRAVERSE_STMT(MSDependentExistsStmt, { DEF_TRAVERSE_STMT(ReturnStmt, { }) DEF_TRAVERSE_STMT(SwitchStmt, { }) DEF_TRAVERSE_STMT(WhileStmt, { }) -DEF_TRAVERSE_STMT(CapturedStmt, { }) DEF_TRAVERSE_STMT(CXXDependentScopeMemberExpr, { TRY_TO(TraverseNestedNameSpecifierLoc(S->getQualifierLoc())); @@ -2141,6 +2149,9 @@ DEF_TRAVERSE_STMT(MSPropertyRefExpr, {}) DEF_TRAVERSE_STMT(SEHTryStmt, {}) DEF_TRAVERSE_STMT(SEHExceptStmt, {}) DEF_TRAVERSE_STMT(SEHFinallyStmt,{}) +DEF_TRAVERSE_STMT(CapturedStmt, { + TRY_TO(TraverseDecl(S->getCapturedDecl())); +}) DEF_TRAVERSE_STMT(CXXOperatorCallExpr, { }) DEF_TRAVERSE_STMT(OpaqueValueExpr, { }) -- 2.40.0