r297093 - [coroutines] Add DependentCoawaitExpr and fix re-building CoroutineBodyStmt.
Eric Fiselier via cfe-commits
cfe-commits at lists.llvm.org
Mon Mar 6 15:38:15 PST 2017
Author: ericwf
Date: Mon Mar 6 17:38:15 2017
New Revision: 297093
URL: http://llvm.org/viewvc/llvm-project?rev=297093&view=rev
Log:
[coroutines] Add DependentCoawaitExpr and fix re-building CoroutineBodyStmt.
Summary:
The changes contained in this patch are:
1. Defines a new AST node `CoawaitDependentExpr` for representing co_await expressions while the promise type is still dependent.
2. Correctly detect and transform the 'co_await' operand to `p.await_transform(<expr>)` when possible.
3. Change the initial/final suspend points to build during the initial parse, so they have the correct operator co_await lookup results.
4. Fix transformation of the CoroutineBodyStmt so that it doesn't re-build the final/initial suspends.
@rsmith: This change is a little big, but it's not trivial for me to split it up. Please let me know if you would prefer this submitted as multiple patches.
Reviewers: rsmith, GorNishanov
Reviewed By: rsmith
Subscribers: ABataev, rsmith, mehdi_amini, cfe-commits
Differential Revision: https://reviews.llvm.org/D26057
Modified:
cfe/trunk/include/clang/AST/ExprCXX.h
cfe/trunk/include/clang/AST/RecursiveASTVisitor.h
cfe/trunk/include/clang/AST/Stmt.h
cfe/trunk/include/clang/AST/StmtCXX.h
cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
cfe/trunk/include/clang/Basic/StmtNodes.td
cfe/trunk/include/clang/Sema/ScopeInfo.h
cfe/trunk/include/clang/Sema/Sema.h
cfe/trunk/lib/AST/Expr.cpp
cfe/trunk/lib/AST/ExprClassification.cpp
cfe/trunk/lib/AST/ExprConstant.cpp
cfe/trunk/lib/AST/ItaniumMangle.cpp
cfe/trunk/lib/AST/StmtPrinter.cpp
cfe/trunk/lib/AST/StmtProfile.cpp
cfe/trunk/lib/Parse/ParseStmt.cpp
cfe/trunk/lib/Sema/ScopeInfo.cpp
cfe/trunk/lib/Sema/SemaCoroutine.cpp
cfe/trunk/lib/Sema/SemaDecl.cpp
cfe/trunk/lib/Sema/SemaExceptionSpec.cpp
cfe/trunk/lib/Sema/TreeTransform.h
cfe/trunk/lib/Serialization/ASTReaderStmt.cpp
cfe/trunk/lib/Serialization/ASTWriterStmt.cpp
cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp
cfe/trunk/test/SemaCXX/coroutines.cpp
cfe/trunk/tools/libclang/CXCursor.cpp
Modified: cfe/trunk/include/clang/AST/ExprCXX.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/ExprCXX.h?rev=297093&r1=297092&r2=297093&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/ExprCXX.h (original)
+++ cfe/trunk/include/clang/AST/ExprCXX.h Mon Mar 6 17:38:15 2017
@@ -4194,11 +4194,16 @@ class CoawaitExpr : public CoroutineSusp
friend class ASTStmtReader;
public:
CoawaitExpr(SourceLocation CoawaitLoc, Expr *Operand, Expr *Ready,
- Expr *Suspend, Expr *Resume)
+ Expr *Suspend, Expr *Resume, bool IsImplicit = false)
: CoroutineSuspendExpr(CoawaitExprClass, CoawaitLoc, Operand, Ready,
- Suspend, Resume) {}
- CoawaitExpr(SourceLocation CoawaitLoc, QualType Ty, Expr *Operand)
- : CoroutineSuspendExpr(CoawaitExprClass, CoawaitLoc, Ty, Operand) {}
+ Suspend, Resume) {
+ CoawaitBits.IsImplicit = IsImplicit;
+ }
+ CoawaitExpr(SourceLocation CoawaitLoc, QualType Ty, Expr *Operand,
+ bool IsImplicit = false)
+ : CoroutineSuspendExpr(CoawaitExprClass, CoawaitLoc, Ty, Operand) {
+ CoawaitBits.IsImplicit = IsImplicit;
+ }
CoawaitExpr(EmptyShell Empty)
: CoroutineSuspendExpr(CoawaitExprClass, Empty) {}
@@ -4207,11 +4212,57 @@ public:
return getCommonExpr();
}
+ bool isImplicit() const { return CoawaitBits.IsImplicit; }
+ void setIsImplicit(bool value = true) { CoawaitBits.IsImplicit = value; }
+
static bool classof(const Stmt *T) {
return T->getStmtClass() == CoawaitExprClass;
}
};
+/// \brief Represents a 'co_await' expression while the type of the promise
+/// is dependent.
+class DependentCoawaitExpr : public Expr {
+ SourceLocation KeywordLoc;
+ Stmt *SubExprs[2];
+
+ friend class ASTStmtReader;
+
+public:
+ DependentCoawaitExpr(SourceLocation KeywordLoc, QualType Ty, Expr *Op,
+ UnresolvedLookupExpr *OpCoawait)
+ : Expr(DependentCoawaitExprClass, Ty, VK_RValue, OK_Ordinary,
+ /*TypeDependent*/ true, /*ValueDependent*/ true,
+ /*InstantiationDependent*/ true,
+ Op->containsUnexpandedParameterPack()),
+ KeywordLoc(KeywordLoc) {
+ assert(Op->isTypeDependent() && Ty->isDependentType() &&
+ "wrong constructor for non-dependent co_await/co_yield expression");
+ SubExprs[0] = Op;
+ SubExprs[1] = OpCoawait;
+ }
+
+ DependentCoawaitExpr(EmptyShell Empty)
+ : Expr(DependentCoawaitExprClass, Empty) {}
+
+ Expr *getOperand() const { return cast<Expr>(SubExprs[0]); }
+ UnresolvedLookupExpr *getOperatorCoawaitLookup() const {
+ return cast<UnresolvedLookupExpr>(SubExprs[1]);
+ }
+ SourceLocation getKeywordLoc() const { return KeywordLoc; }
+
+ SourceLocation getLocStart() const LLVM_READONLY { return KeywordLoc; }
+ SourceLocation getLocEnd() const LLVM_READONLY {
+ return getOperand()->getLocEnd();
+ }
+
+ child_range children() { return child_range(SubExprs, SubExprs + 2); }
+
+ static bool classof(const Stmt *T) {
+ return T->getStmtClass() == DependentCoawaitExprClass;
+ }
+};
+
/// \brief Represents a 'co_yield' expression.
class CoyieldExpr : public CoroutineSuspendExpr {
friend class ASTStmtReader;
Modified: cfe/trunk/include/clang/AST/RecursiveASTVisitor.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/RecursiveASTVisitor.h?rev=297093&r1=297092&r2=297093&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/RecursiveASTVisitor.h (original)
+++ cfe/trunk/include/clang/AST/RecursiveASTVisitor.h Mon Mar 6 17:38:15 2017
@@ -2516,6 +2516,12 @@ DEF_TRAVERSE_STMT(CoawaitExpr, {
ShouldVisitChildren = false;
}
})
+DEF_TRAVERSE_STMT(DependentCoawaitExpr, {
+ if (!getDerived().shouldVisitImplicitCode()) {
+ TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getOperand());
+ ShouldVisitChildren = false;
+ }
+})
DEF_TRAVERSE_STMT(CoyieldExpr, {
if (!getDerived().shouldVisitImplicitCode()) {
TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getOperand());
Modified: cfe/trunk/include/clang/AST/Stmt.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Stmt.h?rev=297093&r1=297092&r2=297093&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/Stmt.h (original)
+++ cfe/trunk/include/clang/AST/Stmt.h Mon Mar 6 17:38:15 2017
@@ -253,6 +253,17 @@ protected:
unsigned NumArgs : 32 - 8 - 1 - NumExprBits;
};
+ class CoawaitExprBitfields {
+ friend class CoawaitExpr;
+
+ unsigned : NumExprBits;
+
+ unsigned IsImplicit : 1;
+
+ /// \brief The number of arguments to this type trait.
+ unsigned NumArgs : 32 - 1 - NumExprBits;
+ };
+
union {
StmtBitfields StmtBits;
CompoundStmtBitfields CompoundStmtBits;
@@ -269,6 +280,7 @@ protected:
ObjCIndirectCopyRestoreExprBitfields ObjCIndirectCopyRestoreExprBits;
InitListExprBitfields InitListExprBits;
TypeTraitExprBitfields TypeTraitExprBits;
+ CoawaitExprBitfields CoawaitBits;
};
friend class ASTStmtReader;
Modified: cfe/trunk/include/clang/AST/StmtCXX.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/StmtCXX.h?rev=297093&r1=297092&r2=297093&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/StmtCXX.h (original)
+++ cfe/trunk/include/clang/AST/StmtCXX.h Mon Mar 6 17:38:15 2017
@@ -370,24 +370,25 @@ public:
}
Expr *getAllocate() const {
- return cast<Expr>(getStoredStmts()[SubStmt::Allocate]);
+ return cast_or_null<Expr>(getStoredStmts()[SubStmt::Allocate]);
}
Expr *getDeallocate() const {
- return cast<Expr>(getStoredStmts()[SubStmt::Deallocate]);
+ return cast_or_null<Expr>(getStoredStmts()[SubStmt::Deallocate]);
}
Expr *getReturnValueInit() const {
- return cast<Expr>(getStoredStmts()[SubStmt::ReturnValue]);
+ return cast_or_null<Expr>(getStoredStmts()[SubStmt::ReturnValue]);
}
ArrayRef<Stmt const *> getParamMoves() const {
return {getStoredStmts() + SubStmt::FirstParamMove, NumParams};
}
SourceLocation getLocStart() const LLVM_READONLY {
- return getBody()->getLocStart();
+ return getBody() ? getBody()->getLocStart()
+ : getPromiseDecl()->getLocStart();
}
SourceLocation getLocEnd() const LLVM_READONLY {
- return getBody()->getLocEnd();
+ return getBody() ? getBody()->getLocEnd() : getPromiseDecl()->getLocEnd();
}
child_range children() {
@@ -417,10 +418,14 @@ class CoreturnStmt : public Stmt {
enum SubStmt { Operand, PromiseCall, Count };
Stmt *SubStmts[SubStmt::Count];
+ bool IsImplicit : 1;
+
friend class ASTStmtReader;
public:
- CoreturnStmt(SourceLocation CoreturnLoc, Stmt *Operand, Stmt *PromiseCall)
- : Stmt(CoreturnStmtClass), CoreturnLoc(CoreturnLoc) {
+ CoreturnStmt(SourceLocation CoreturnLoc, Stmt *Operand, Stmt *PromiseCall,
+ bool IsImplicit = false)
+ : Stmt(CoreturnStmtClass), CoreturnLoc(CoreturnLoc),
+ IsImplicit(IsImplicit) {
SubStmts[SubStmt::Operand] = Operand;
SubStmts[SubStmt::PromiseCall] = PromiseCall;
}
@@ -438,6 +443,9 @@ public:
return static_cast<Expr*>(SubStmts[PromiseCall]);
}
+ bool isImplicit() const { return IsImplicit; }
+ void setIsImplicit(bool value = true) { IsImplicit = value; }
+
SourceLocation getLocStart() const LLVM_READONLY { return CoreturnLoc; }
SourceLocation getLocEnd() const LLVM_READONLY {
return getOperand() ? getOperand()->getLocEnd() : getLocStart();
Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=297093&r1=297092&r2=297093&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Mon Mar 6 17:38:15 2017
@@ -8816,8 +8816,7 @@ let CategoryName = "Coroutines Issue" in
def err_return_in_coroutine : Error<
"return statement not allowed in coroutine; did you mean 'co_return'?">;
def note_declared_coroutine_here : Note<
- "function is a coroutine due to use of "
- "'%select{co_await|co_yield|co_return}0' here">;
+ "function is a coroutine due to use of '%0' here">;
def err_coroutine_objc_method : Error<
"Objective-C methods as coroutines are not yet supported">;
def err_coroutine_unevaluated_context : Error<
@@ -8849,6 +8848,11 @@ def err_malformed_std_current_exception
"'std::current_exception' must be a function">;
def err_coroutine_promise_return_ill_formed : Error<
"%0 declares both 'return_value' and 'return_void'">;
+def note_coroutine_promise_implicit_await_transform_required_here : Note<
+ "call to 'await_transform' implicitly required by 'co_await' here">;
+def note_coroutine_promise_call_implicitly_required : Note<
+ "call to '%select{initial_suspend|final_suspend}0' implicitly "
+ "required by the %select{initial suspend point|final suspend point}0">;
}
let CategoryName = "Documentation Issue" in {
Modified: cfe/trunk/include/clang/Basic/StmtNodes.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/StmtNodes.td?rev=297093&r1=297092&r2=297093&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/StmtNodes.td (original)
+++ cfe/trunk/include/clang/Basic/StmtNodes.td Mon Mar 6 17:38:15 2017
@@ -150,6 +150,7 @@ def CXXFoldExpr : DStmt<Expr>;
// C++ Coroutines TS expressions
def CoroutineSuspendExpr : DStmt<Expr, 1>;
def CoawaitExpr : DStmt<CoroutineSuspendExpr>;
+def DependentCoawaitExpr : DStmt<Expr>;
def CoyieldExpr : DStmt<CoroutineSuspendExpr>;
// Obj-C Expressions.
Modified: cfe/trunk/include/clang/Sema/ScopeInfo.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/ScopeInfo.h?rev=297093&r1=297092&r2=297093&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/ScopeInfo.h (original)
+++ cfe/trunk/include/clang/Sema/ScopeInfo.h Mon Mar 6 17:38:15 2017
@@ -135,6 +135,10 @@ public:
/// false if there is an invocation of an initializer on 'self'.
bool ObjCWarnForNoInitDelegation : 1;
+ /// \brief True only when this function has not already built, or attempted
+ /// to build, the initial and final coroutine suspend points
+ bool NeedsCoroutineSuspends : 1;
+
/// First 'return' statement in the current function.
SourceLocation FirstReturnLoc;
@@ -159,6 +163,9 @@ public:
/// \brief The promise object for this coroutine, if any.
VarDecl *CoroutinePromise = nullptr;
+ /// \brief The initial and final coroutine suspend points.
+ std::pair<Stmt *, Stmt *> CoroutineSuspends;
+
/// \brief The list of coroutine control flow constructs (co_await, co_yield,
/// co_return) that occur within the function or block. Empty if and only if
/// this function or block is not (yet known to be) a coroutine.
@@ -376,7 +383,25 @@ public:
(HasIndirectGoto ||
(HasBranchProtectedScope && HasBranchIntoScope));
}
-
+
+ void setNeedsCoroutineSuspends(bool value = true) {
+ assert((!value || CoroutineSuspends.first == nullptr) &&
+ "we already have valid suspend points");
+ NeedsCoroutineSuspends = value;
+ }
+
+ bool hasInvalidCoroutineSuspends() const {
+ return !NeedsCoroutineSuspends && CoroutineSuspends.first == nullptr;
+ }
+
+ void setCoroutineSuspends(Stmt *Initial, Stmt *Final) {
+ assert(Initial && Final && "suspend points cannot be null");
+ assert(CoroutineSuspends.first == nullptr && "suspend points already set");
+ NeedsCoroutineSuspends = false;
+ CoroutineSuspends.first = Initial;
+ CoroutineSuspends.second = Final;
+ }
+
FunctionScopeInfo(DiagnosticsEngine &Diag)
: Kind(SK_Function),
HasBranchProtectedScope(false),
@@ -386,6 +411,7 @@ public:
HasOMPDeclareReductionCombiner(false),
HasFallthroughStmt(false),
HasPotentialAvailabilityViolations(false),
+ NeedsCoroutineSuspends(true),
ObjCShouldCallSuper(false),
ObjCIsDesignatedInit(false),
ObjCWarnForNoDesignatedInitChain(false),
Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=297093&r1=297092&r2=297093&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Mon Mar 6 17:38:15 2017
@@ -26,6 +26,7 @@
#include "clang/AST/MangleNumberingContext.h"
#include "clang/AST/NSAPI.h"
#include "clang/AST/PrettyPrinter.h"
+#include "clang/AST/StmtCXX.h"
#include "clang/AST/TypeLoc.h"
#include "clang/AST/TypeOrdering.h"
#include "clang/Basic/ExpressionTraits.h"
@@ -101,6 +102,7 @@ namespace clang {
class CodeCompletionAllocator;
class CodeCompletionTUInfo;
class CodeCompletionResult;
+ class CoroutineBodyStmt;
class Decl;
class DeclAccessPair;
class DeclContext;
@@ -8188,12 +8190,17 @@ public:
//
ExprResult ActOnCoawaitExpr(Scope *S, SourceLocation KwLoc, Expr *E);
ExprResult ActOnCoyieldExpr(Scope *S, SourceLocation KwLoc, Expr *E);
- StmtResult ActOnCoreturnStmt(SourceLocation KwLoc, Expr *E);
+ StmtResult ActOnCoreturnStmt(Scope *S, SourceLocation KwLoc, Expr *E);
- ExprResult BuildCoawaitExpr(SourceLocation KwLoc, Expr *E);
+ ExprResult BuildResolvedCoawaitExpr(SourceLocation KwLoc, Expr *E,
+ bool IsImplicit = false);
+ ExprResult BuildUnresolvedCoawaitExpr(SourceLocation KwLoc, Expr *E,
+ UnresolvedLookupExpr* Lookup);
ExprResult BuildCoyieldExpr(SourceLocation KwLoc, Expr *E);
- StmtResult BuildCoreturnStmt(SourceLocation KwLoc, Expr *E);
-
+ StmtResult BuildCoreturnStmt(SourceLocation KwLoc, Expr *E,
+ bool IsImplicit = false);
+ StmtResult BuildCoroutineBodyStmt(CoroutineBodyStmt::CtorArgs);
+ VarDecl *buildCoroutinePromise(SourceLocation Loc);
void CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *&Body);
//===--------------------------------------------------------------------===//
Modified: cfe/trunk/lib/AST/Expr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Expr.cpp?rev=297093&r1=297092&r2=297093&view=diff
==============================================================================
--- cfe/trunk/lib/AST/Expr.cpp (original)
+++ cfe/trunk/lib/AST/Expr.cpp Mon Mar 6 17:38:15 2017
@@ -2958,6 +2958,7 @@ bool Expr::HasSideEffects(const ASTConte
case CXXNewExprClass:
case CXXDeleteExprClass:
case CoawaitExprClass:
+ case DependentCoawaitExprClass:
case CoyieldExprClass:
// These always have a side-effect.
return true;
Modified: cfe/trunk/lib/AST/ExprClassification.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprClassification.cpp?rev=297093&r1=297092&r2=297093&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ExprClassification.cpp (original)
+++ cfe/trunk/lib/AST/ExprClassification.cpp Mon Mar 6 17:38:15 2017
@@ -129,6 +129,7 @@ static Cl::Kinds ClassifyInternal(ASTCon
case Expr::UnresolvedLookupExprClass:
case Expr::UnresolvedMemberExprClass:
case Expr::TypoExprClass:
+ case Expr::DependentCoawaitExprClass:
case Expr::CXXDependentScopeMemberExprClass:
case Expr::DependentScopeDeclRefExprClass:
// ObjC instance variables are lvalues
Modified: cfe/trunk/lib/AST/ExprConstant.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprConstant.cpp?rev=297093&r1=297092&r2=297093&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ExprConstant.cpp (original)
+++ cfe/trunk/lib/AST/ExprConstant.cpp Mon Mar 6 17:38:15 2017
@@ -10216,6 +10216,7 @@ static ICEDiag CheckICE(const Expr* E, c
case Expr::LambdaExprClass:
case Expr::CXXFoldExprClass:
case Expr::CoawaitExprClass:
+ case Expr::DependentCoawaitExprClass:
case Expr::CoyieldExprClass:
return ICEDiag(IK_NotICE, E->getLocStart());
Modified: cfe/trunk/lib/AST/ItaniumMangle.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ItaniumMangle.cpp?rev=297093&r1=297092&r2=297093&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ItaniumMangle.cpp (original)
+++ cfe/trunk/lib/AST/ItaniumMangle.cpp Mon Mar 6 17:38:15 2017
@@ -4034,6 +4034,12 @@ recurse:
mangleExpression(cast<CoawaitExpr>(E)->getOperand());
break;
+ case Expr::DependentCoawaitExprClass:
+ // FIXME: Propose a non-vendor mangling.
+ Out << "v18co_await";
+ mangleExpression(cast<DependentCoawaitExpr>(E)->getOperand());
+ break;
+
case Expr::CoyieldExprClass:
// FIXME: Propose a non-vendor mangling.
Out << "v18co_yield";
Modified: cfe/trunk/lib/AST/StmtPrinter.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/StmtPrinter.cpp?rev=297093&r1=297092&r2=297093&view=diff
==============================================================================
--- cfe/trunk/lib/AST/StmtPrinter.cpp (original)
+++ cfe/trunk/lib/AST/StmtPrinter.cpp Mon Mar 6 17:38:15 2017
@@ -2475,6 +2475,13 @@ void StmtPrinter::VisitCoawaitExpr(Coawa
PrintExpr(S->getOperand());
}
+
+void StmtPrinter::VisitDependentCoawaitExpr(DependentCoawaitExpr *S) {
+ OS << "co_await ";
+ PrintExpr(S->getOperand());
+}
+
+
void StmtPrinter::VisitCoyieldExpr(CoyieldExpr *S) {
OS << "co_yield ";
PrintExpr(S->getOperand());
Modified: cfe/trunk/lib/AST/StmtProfile.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/StmtProfile.cpp?rev=297093&r1=297092&r2=297093&view=diff
==============================================================================
--- cfe/trunk/lib/AST/StmtProfile.cpp (original)
+++ cfe/trunk/lib/AST/StmtProfile.cpp Mon Mar 6 17:38:15 2017
@@ -1725,6 +1725,10 @@ void StmtProfiler::VisitCoawaitExpr(cons
VisitExpr(S);
}
+void StmtProfiler::VisitDependentCoawaitExpr(const DependentCoawaitExpr *S) {
+ VisitExpr(S);
+}
+
void StmtProfiler::VisitCoyieldExpr(const CoyieldExpr *S) {
VisitExpr(S);
}
Modified: cfe/trunk/lib/Parse/ParseStmt.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseStmt.cpp?rev=297093&r1=297092&r2=297093&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseStmt.cpp (original)
+++ cfe/trunk/lib/Parse/ParseStmt.cpp Mon Mar 6 17:38:15 2017
@@ -1898,7 +1898,7 @@ StmtResult Parser::ParseReturnStatement(
}
}
if (IsCoreturn)
- return Actions.ActOnCoreturnStmt(ReturnLoc, R.get());
+ return Actions.ActOnCoreturnStmt(getCurScope(), ReturnLoc, R.get());
return Actions.ActOnReturnStmt(ReturnLoc, R.get(), getCurScope());
}
Modified: cfe/trunk/lib/Sema/ScopeInfo.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/ScopeInfo.cpp?rev=297093&r1=297092&r2=297093&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/ScopeInfo.cpp (original)
+++ cfe/trunk/lib/Sema/ScopeInfo.cpp Mon Mar 6 17:38:15 2017
@@ -43,6 +43,9 @@ void FunctionScopeInfo::Clear() {
SwitchStack.clear();
Returns.clear();
CoroutinePromise = nullptr;
+ NeedsCoroutineSuspends = true;
+ CoroutineSuspends.first = nullptr;
+ CoroutineSuspends.second = nullptr;
CoroutineStmts.clear();
ErrorTrap.reset();
PossiblyUnreachableDiags.clear();
Modified: cfe/trunk/lib/Sema/SemaCoroutine.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaCoroutine.cpp?rev=297093&r1=297092&r2=297093&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaCoroutine.cpp (original)
+++ cfe/trunk/lib/Sema/SemaCoroutine.cpp Mon Mar 6 17:38:15 2017
@@ -21,6 +21,16 @@
using namespace clang;
using namespace sema;
+static bool lookupMember(Sema &S, const char *Name, CXXRecordDecl *RD,
+ SourceLocation Loc) {
+ DeclarationName DN = S.PP.getIdentifierInfo(Name);
+ LookupResult LR(S, DN, Loc, Sema::LookupMemberName);
+ // Suppress diagnostics when a private member is selected. The same warnings
+ // will be produced again when building the call.
+ LR.suppressDiagnostics();
+ return S.LookupQualifiedName(LR, RD);
+}
+
/// Look up the std::coroutine_traits<...>::promise_type for the given
/// function type.
static QualType lookupPromiseType(Sema &S, const FunctionProtoType *FnType,
@@ -167,42 +177,48 @@ static bool isValidCoroutineContext(Sema
return !Diagnosed;
}
-/// Check that this is a context in which a coroutine suspension can appear.
-static FunctionScopeInfo *checkCoroutineContext(Sema &S, SourceLocation Loc,
- StringRef Keyword) {
- if (!isValidCoroutineContext(S, Loc, Keyword))
- return nullptr;
-
- assert(isa<FunctionDecl>(S.CurContext) && "not in a function scope");
- auto *FD = cast<FunctionDecl>(S.CurContext);
- auto *ScopeInfo = S.getCurFunction();
- assert(ScopeInfo && "missing function scope for function");
+static ExprResult buildOperatorCoawaitLookupExpr(Sema &SemaRef, Scope *S,
+ SourceLocation Loc) {
+ DeclarationName OpName =
+ SemaRef.Context.DeclarationNames.getCXXOperatorName(OO_Coawait);
+ LookupResult Operators(SemaRef, OpName, SourceLocation(),
+ Sema::LookupOperatorName);
+ SemaRef.LookupName(Operators, S);
+
+ assert(!Operators.isAmbiguous() && "Operator lookup cannot be ambiguous");
+ const auto &Functions = Operators.asUnresolvedSet();
+ bool IsOverloaded =
+ Functions.size() > 1 ||
+ (Functions.size() == 1 && isa<FunctionTemplateDecl>(*Functions.begin()));
+ Expr *CoawaitOp = UnresolvedLookupExpr::Create(
+ SemaRef.Context, /*NamingClass*/ nullptr, NestedNameSpecifierLoc(),
+ DeclarationNameInfo(OpName, Loc), /*RequiresADL*/ true, IsOverloaded,
+ Functions.begin(), Functions.end());
+ assert(CoawaitOp);
+ return CoawaitOp;
+}
- // If we don't have a promise variable, build one now.
- if (!ScopeInfo->CoroutinePromise) {
- QualType T = FD->getType()->isDependentType()
- ? S.Context.DependentTy
- : lookupPromiseType(
- S, FD->getType()->castAs<FunctionProtoType>(),
- Loc, FD->getLocation());
- if (T.isNull())
- return nullptr;
-
- // Create and default-initialize the promise.
- ScopeInfo->CoroutinePromise =
- VarDecl::Create(S.Context, FD, FD->getLocation(), FD->getLocation(),
- &S.PP.getIdentifierTable().get("__promise"), T,
- S.Context.getTrivialTypeSourceInfo(T, Loc), SC_None);
- S.CheckVariableDeclarationType(ScopeInfo->CoroutinePromise);
- if (!ScopeInfo->CoroutinePromise->isInvalidDecl())
- S.ActOnUninitializedDecl(ScopeInfo->CoroutinePromise);
- }
+/// Build a call to 'operator co_await' if there is a suitable operator for
+/// the given expression.
+static ExprResult buildOperatorCoawaitCall(Sema &SemaRef, SourceLocation Loc,
+ Expr *E,
+ UnresolvedLookupExpr *Lookup) {
+ UnresolvedSet<16> Functions;
+ Functions.append(Lookup->decls_begin(), Lookup->decls_end());
+ return SemaRef.CreateOverloadedUnaryOp(Loc, UO_Coawait, Functions, E);
+}
- return ScopeInfo;
+static ExprResult buildOperatorCoawaitCall(Sema &SemaRef, Scope *S,
+ SourceLocation Loc, Expr *E) {
+ ExprResult R = buildOperatorCoawaitLookupExpr(SemaRef, S, Loc);
+ if (R.isInvalid())
+ return ExprError();
+ return buildOperatorCoawaitCall(SemaRef, Loc, E,
+ cast<UnresolvedLookupExpr>(R.get()));
}
static Expr *buildBuiltinCall(Sema &S, SourceLocation Loc, Builtin::ID Id,
- MutableArrayRef<Expr *> CallArgs) {
+ MultiExprArg CallArgs) {
StringRef Name = S.Context.BuiltinInfo.getName(Id);
LookupResult R(S, &S.Context.Idents.get(Name), Loc, Sema::LookupOrdinaryName);
S.LookupName(R, S.TUScope, /*AllowBuiltinCreation=*/true);
@@ -221,15 +237,6 @@ static Expr *buildBuiltinCall(Sema &S, S
return Call.get();
}
-/// Build a call to 'operator co_await' if there is a suitable operator for
-/// the given expression.
-static ExprResult buildOperatorCoawaitCall(Sema &SemaRef, Scope *S,
- SourceLocation Loc, Expr *E) {
- UnresolvedSet<16> Functions;
- SemaRef.LookupOverloadedOperatorName(OO_Coawait, S, E->getType(), QualType(),
- Functions);
- return SemaRef.CreateOverloadedUnaryOp(Loc, UO_Coawait, Functions, E);
-}
struct ReadySuspendResumeResult {
bool IsInvalid;
@@ -237,8 +244,7 @@ struct ReadySuspendResumeResult {
};
static ExprResult buildMemberCall(Sema &S, Expr *Base, SourceLocation Loc,
- StringRef Name,
- MutableArrayRef<Expr *> Args) {
+ StringRef Name, MultiExprArg Args) {
DeclarationNameInfo NameInfo(&S.PP.getIdentifierTable().get(Name), Loc);
// FIXME: Fix BuildMemberReferenceExpr to take a const CXXScopeSpec&.
@@ -276,25 +282,174 @@ static ReadySuspendResumeResult buildCoa
return Calls;
}
+static ExprResult buildPromiseCall(Sema &S, VarDecl *Promise,
+ SourceLocation Loc, StringRef Name,
+ MultiExprArg Args) {
+
+ // Form a reference to the promise.
+ ExprResult PromiseRef = S.BuildDeclRefExpr(
+ Promise, Promise->getType().getNonReferenceType(), VK_LValue, Loc);
+ if (PromiseRef.isInvalid())
+ return ExprError();
+
+ // Call 'yield_value', passing in E.
+ return buildMemberCall(S, PromiseRef.get(), Loc, Name, Args);
+}
+
+VarDecl *Sema::buildCoroutinePromise(SourceLocation Loc) {
+ assert(isa<FunctionDecl>(CurContext) && "not in a function scope");
+ auto *FD = cast<FunctionDecl>(CurContext);
+
+ QualType T =
+ FD->getType()->isDependentType()
+ ? Context.DependentTy
+ : lookupPromiseType(*this, FD->getType()->castAs<FunctionProtoType>(),
+ Loc, FD->getLocation());
+ if (T.isNull())
+ return nullptr;
+
+ auto *VD = VarDecl::Create(Context, FD, FD->getLocation(), FD->getLocation(),
+ &PP.getIdentifierTable().get("__promise"), T,
+ Context.getTrivialTypeSourceInfo(T, Loc), SC_None);
+ CheckVariableDeclarationType(VD);
+ if (VD->isInvalidDecl())
+ return nullptr;
+ ActOnUninitializedDecl(VD);
+ assert(!VD->isInvalidDecl());
+ return VD;
+}
+
+/// Check that this is a context in which a coroutine suspension can appear.
+static FunctionScopeInfo *checkCoroutineContext(Sema &S, SourceLocation Loc,
+ StringRef Keyword) {
+ if (!isValidCoroutineContext(S, Loc, Keyword))
+ return nullptr;
+
+ assert(isa<FunctionDecl>(S.CurContext) && "not in a function scope");
+ auto *FD = cast<FunctionDecl>(S.CurContext);
+
+ auto *ScopeInfo = S.getCurFunction();
+ assert(ScopeInfo && "missing function scope for function");
+
+ if (ScopeInfo->CoroutinePromise)
+ return ScopeInfo;
+
+ ScopeInfo->CoroutinePromise = S.buildCoroutinePromise(Loc);
+ if (!ScopeInfo->CoroutinePromise)
+ return nullptr;
+
+ return ScopeInfo;
+}
+
+static bool actOnCoroutineBodyStart(Sema &S, Scope *SC, SourceLocation KWLoc,
+ StringRef Keyword) {
+ if (!checkCoroutineContext(S, KWLoc, Keyword))
+ return false;
+ auto *ScopeInfo = S.getCurFunction();
+ assert(ScopeInfo->CoroutinePromise);
+
+ // If we have existing coroutine statements then we have already built
+ // the initial and final suspend points.
+ if (!ScopeInfo->NeedsCoroutineSuspends)
+ return true;
+
+ ScopeInfo->setNeedsCoroutineSuspends(false);
+
+ auto *Fn = cast<FunctionDecl>(S.CurContext);
+ SourceLocation Loc = Fn->getLocation();
+ // Build the initial suspend point
+ auto buildSuspends = [&](StringRef Name) mutable -> StmtResult {
+ ExprResult Suspend =
+ buildPromiseCall(S, ScopeInfo->CoroutinePromise, Loc, Name, None);
+ if (Suspend.isInvalid())
+ return StmtError();
+ Suspend = buildOperatorCoawaitCall(S, SC, Loc, Suspend.get());
+ if (Suspend.isInvalid())
+ return StmtError();
+ Suspend = S.BuildResolvedCoawaitExpr(Loc, Suspend.get(),
+ /*IsImplicit*/ true);
+ Suspend = S.ActOnFinishFullExpr(Suspend.get());
+ if (Suspend.isInvalid()) {
+ S.Diag(Loc, diag::note_coroutine_promise_call_implicitly_required)
+ << ((Name == "initial_suspend") ? 0 : 1);
+ S.Diag(KWLoc, diag::note_declared_coroutine_here) << Keyword;
+ return StmtError();
+ }
+ return cast<Stmt>(Suspend.get());
+ };
+
+ StmtResult InitSuspend = buildSuspends("initial_suspend");
+ if (InitSuspend.isInvalid())
+ return true;
+
+ StmtResult FinalSuspend = buildSuspends("final_suspend");
+ if (FinalSuspend.isInvalid())
+ return true;
+
+ ScopeInfo->setCoroutineSuspends(InitSuspend.get(), FinalSuspend.get());
+
+ return true;
+}
+
ExprResult Sema::ActOnCoawaitExpr(Scope *S, SourceLocation Loc, Expr *E) {
- auto *Coroutine = checkCoroutineContext(*this, Loc, "co_await");
- if (!Coroutine) {
+ if (!actOnCoroutineBodyStart(*this, S, Loc, "co_await")) {
CorrectDelayedTyposInExpr(E);
return ExprError();
}
+
if (E->getType()->isPlaceholderType()) {
ExprResult R = CheckPlaceholderExpr(E);
if (R.isInvalid()) return ExprError();
E = R.get();
}
+ ExprResult Lookup = buildOperatorCoawaitLookupExpr(*this, S, Loc);
+ if (Lookup.isInvalid())
+ return ExprError();
+ return BuildUnresolvedCoawaitExpr(Loc, E,
+ cast<UnresolvedLookupExpr>(Lookup.get()));
+}
+
+ExprResult Sema::BuildUnresolvedCoawaitExpr(SourceLocation Loc, Expr *E,
+ UnresolvedLookupExpr *Lookup) {
+ auto *FSI = checkCoroutineContext(*this, Loc, "co_await");
+ if (!FSI)
+ return ExprError();
- ExprResult Awaitable = buildOperatorCoawaitCall(*this, S, Loc, E);
+ if (E->getType()->isPlaceholderType()) {
+ ExprResult R = CheckPlaceholderExpr(E);
+ if (R.isInvalid())
+ return ExprError();
+ E = R.get();
+ }
+
+ auto *Promise = FSI->CoroutinePromise;
+ if (Promise->getType()->isDependentType()) {
+ Expr *Res =
+ new (Context) DependentCoawaitExpr(Loc, Context.DependentTy, E, Lookup);
+ FSI->CoroutineStmts.push_back(Res);
+ return Res;
+ }
+
+ auto *RD = Promise->getType()->getAsCXXRecordDecl();
+ if (lookupMember(*this, "await_transform", RD, Loc)) {
+ ExprResult R = buildPromiseCall(*this, Promise, Loc, "await_transform", E);
+ if (R.isInvalid()) {
+ Diag(Loc,
+ diag::note_coroutine_promise_implicit_await_transform_required_here)
+ << E->getSourceRange();
+ return ExprError();
+ }
+ E = R.get();
+ }
+ ExprResult Awaitable = buildOperatorCoawaitCall(*this, Loc, E, Lookup);
if (Awaitable.isInvalid())
return ExprError();
- return BuildCoawaitExpr(Loc, Awaitable.get());
+ return BuildResolvedCoawaitExpr(Loc, Awaitable.get());
}
-ExprResult Sema::BuildCoawaitExpr(SourceLocation Loc, Expr *E) {
+
+ExprResult Sema::BuildResolvedCoawaitExpr(SourceLocation Loc, Expr *E,
+ bool IsImplicit) {
auto *Coroutine = checkCoroutineContext(*this, Loc, "co_await");
if (!Coroutine)
return ExprError();
@@ -306,8 +461,10 @@ ExprResult Sema::BuildCoawaitExpr(Source
}
if (E->getType()->isDependentType()) {
- Expr *Res = new (Context) CoawaitExpr(Loc, Context.DependentTy, E);
- Coroutine->CoroutineStmts.push_back(Res);
+ Expr *Res = new (Context)
+ CoawaitExpr(Loc, Context.DependentTy, E, IsImplicit);
+ if (!IsImplicit)
+ Coroutine->CoroutineStmts.push_back(Res);
return Res;
}
@@ -322,37 +479,21 @@ ExprResult Sema::BuildCoawaitExpr(Source
return ExprError();
Expr *Res = new (Context) CoawaitExpr(Loc, E, RSS.Results[0], RSS.Results[1],
- RSS.Results[2]);
- Coroutine->CoroutineStmts.push_back(Res);
+ RSS.Results[2], IsImplicit);
+ if (!IsImplicit)
+ Coroutine->CoroutineStmts.push_back(Res);
return Res;
}
-static ExprResult buildPromiseCall(Sema &S, FunctionScopeInfo *Coroutine,
- SourceLocation Loc, StringRef Name,
- MutableArrayRef<Expr *> Args) {
- assert(Coroutine->CoroutinePromise && "no promise for coroutine");
-
- // Form a reference to the promise.
- auto *Promise = Coroutine->CoroutinePromise;
- ExprResult PromiseRef = S.BuildDeclRefExpr(
- Promise, Promise->getType().getNonReferenceType(), VK_LValue, Loc);
- if (PromiseRef.isInvalid())
- return ExprError();
-
- // Call 'yield_value', passing in E.
- return buildMemberCall(S, PromiseRef.get(), Loc, Name, Args);
-}
-
ExprResult Sema::ActOnCoyieldExpr(Scope *S, SourceLocation Loc, Expr *E) {
- auto *Coroutine = checkCoroutineContext(*this, Loc, "co_yield");
- if (!Coroutine) {
+ if (!actOnCoroutineBodyStart(*this, S, Loc, "co_yield")) {
CorrectDelayedTyposInExpr(E);
return ExprError();
}
// Build yield_value call.
- ExprResult Awaitable =
- buildPromiseCall(*this, Coroutine, Loc, "yield_value", E);
+ ExprResult Awaitable = buildPromiseCall(
+ *this, getCurFunction()->CoroutinePromise, Loc, "yield_value", E);
if (Awaitable.isInvalid())
return ExprError();
@@ -396,18 +537,18 @@ ExprResult Sema::BuildCoyieldExpr(Source
return Res;
}
-StmtResult Sema::ActOnCoreturnStmt(SourceLocation Loc, Expr *E) {
- auto *Coroutine = checkCoroutineContext(*this, Loc, "co_return");
- if (!Coroutine) {
+StmtResult Sema::ActOnCoreturnStmt(Scope *S, SourceLocation Loc, Expr *E) {
+ if (!actOnCoroutineBodyStart(*this, S, Loc, "co_return")) {
CorrectDelayedTyposInExpr(E);
return StmtError();
}
return BuildCoreturnStmt(Loc, E);
}
-StmtResult Sema::BuildCoreturnStmt(SourceLocation Loc, Expr *E) {
- auto *Coroutine = checkCoroutineContext(*this, Loc, "co_return");
- if (!Coroutine)
+StmtResult Sema::BuildCoreturnStmt(SourceLocation Loc, Expr *E,
+ bool IsImplicit) {
+ auto *FSI = checkCoroutineContext(*this, Loc, "co_return");
+ if (!FSI)
return StmtError();
if (E && E->getType()->isPlaceholderType() &&
@@ -420,20 +561,22 @@ StmtResult Sema::BuildCoreturnStmt(Sourc
// FIXME: If the operand is a reference to a variable that's about to go out
// of scope, we should treat the operand as an xvalue for this overload
// resolution.
+ VarDecl *Promise = FSI->CoroutinePromise;
ExprResult PC;
if (E && (isa<InitListExpr>(E) || !E->getType()->isVoidType())) {
- PC = buildPromiseCall(*this, Coroutine, Loc, "return_value", E);
+ PC = buildPromiseCall(*this, Promise, Loc, "return_value", E);
} else {
E = MakeFullDiscardedValueExpr(E).get();
- PC = buildPromiseCall(*this, Coroutine, Loc, "return_void", None);
+ PC = buildPromiseCall(*this, Promise, Loc, "return_void", None);
}
if (PC.isInvalid())
return StmtError();
Expr *PCE = ActOnFinishFullExpr(PC.get()).get();
- Stmt *Res = new (Context) CoreturnStmt(Loc, E, PCE);
- Coroutine->CoroutineStmts.push_back(Res);
+ Stmt *Res = new (Context) CoreturnStmt(Loc, E, PCE, IsImplicit);
+ if (!IsImplicit)
+ FSI->CoroutineStmts.push_back(Res);
return Res;
}
@@ -490,88 +633,6 @@ static FunctionDecl *findDeleteForPromis
return OperatorDelete;
}
-// Builds allocation and deallocation for the coroutine. Returns false on
-// failure.
-static bool buildAllocationAndDeallocation(Sema &S, SourceLocation Loc,
- FunctionScopeInfo *Fn,
- Expr *&Allocation,
- Expr *&Deallocation) {
- TypeSourceInfo *TInfo = Fn->CoroutinePromise->getTypeSourceInfo();
- QualType PromiseType = TInfo->getType();
- if (PromiseType->isDependentType())
- return true;
-
- if (S.RequireCompleteType(Loc, PromiseType, diag::err_incomplete_type))
- return false;
-
- // FIXME: Add support for get_return_object_on_allocation failure.
- // FIXME: Add support for stateful allocators.
-
- FunctionDecl *OperatorNew = nullptr;
- FunctionDecl *OperatorDelete = nullptr;
- FunctionDecl *UnusedResult = nullptr;
- bool PassAlignment = false;
-
- S.FindAllocationFunctions(Loc, SourceRange(),
- /*UseGlobal*/ false, PromiseType,
- /*isArray*/ false, PassAlignment,
- /*PlacementArgs*/ None, OperatorNew, UnusedResult);
-
- OperatorDelete = findDeleteForPromise(S, Loc, PromiseType);
-
- if (!OperatorDelete || !OperatorNew)
- return false;
-
- Expr *FramePtr =
- buildBuiltinCall(S, Loc, Builtin::BI__builtin_coro_frame, {});
-
- Expr *FrameSize =
- buildBuiltinCall(S, Loc, Builtin::BI__builtin_coro_size, {});
-
- // Make new call.
-
- ExprResult NewRef =
- S.BuildDeclRefExpr(OperatorNew, OperatorNew->getType(), VK_LValue, Loc);
- if (NewRef.isInvalid())
- return false;
-
- ExprResult NewExpr =
- S.ActOnCallExpr(S.getCurScope(), NewRef.get(), Loc, FrameSize, Loc);
- if (NewExpr.isInvalid())
- return false;
-
- Allocation = NewExpr.get();
-
- // Make delete call.
-
- QualType OpDeleteQualType = OperatorDelete->getType();
-
- ExprResult DeleteRef =
- S.BuildDeclRefExpr(OperatorDelete, OpDeleteQualType, VK_LValue, Loc);
- if (DeleteRef.isInvalid())
- return false;
-
- Expr *CoroFree =
- buildBuiltinCall(S, Loc, Builtin::BI__builtin_coro_free, {FramePtr});
-
- SmallVector<Expr *, 2> DeleteArgs{CoroFree};
-
- // Check if we need to pass the size.
- const auto *OpDeleteType =
- OpDeleteQualType.getTypePtr()->getAs<FunctionProtoType>();
- if (OpDeleteType->getNumParams() > 1)
- DeleteArgs.push_back(FrameSize);
-
- ExprResult DeleteExpr =
- S.ActOnCallExpr(S.getCurScope(), DeleteRef.get(), Loc, DeleteArgs, Loc);
- if (DeleteExpr.isInvalid())
- return false;
-
- Deallocation = DeleteExpr.get();
-
- return true;
-}
-
namespace {
class SubStmtBuilder : public CoroutineBodyStmt::CtorArgs {
Sema &S;
@@ -595,17 +656,16 @@ public:
PromiseRecordDecl = Fn.CoroutinePromise->getType()->getAsCXXRecordDecl();
assert(PromiseRecordDecl && "Type should have already been checked");
}
- this->IsValid = makePromiseStmt() && makeInitialSuspend() &&
- makeFinalSuspend() && makeOnException() &&
- makeOnFallthrough() && makeNewAndDeleteExpr() &&
- makeReturnObject() && makeParamMoves();
+ this->IsValid = makePromiseStmt() && makeInitialAndFinalSuspend() &&
+ makeOnException() && makeOnFallthrough() &&
+ makeNewAndDeleteExpr() && makeReturnObject() &&
+ makeParamMoves();
}
bool isInvalid() const { return !this->IsValid; }
bool makePromiseStmt();
- bool makeInitialSuspend();
- bool makeFinalSuspend();
+ bool makeInitialAndFinalSuspend();
bool makeNewAndDeleteExpr();
bool makeOnFallthrough();
bool makeOnException();
@@ -616,7 +676,7 @@ public:
void Sema::CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *&Body) {
FunctionScopeInfo *Fn = getCurFunction();
- assert(Fn && !Fn->CoroutineStmts.empty() && "not a coroutine");
+ assert(Fn && Fn->CoroutinePromise && "not a coroutine");
// Coroutines [stmt.return]p1:
// A return statement shall not appear in a coroutine.
@@ -624,8 +684,8 @@ void Sema::CheckCompletedCoroutineBody(F
Diag(Fn->FirstReturnLoc, diag::err_return_in_coroutine);
auto *First = Fn->CoroutineStmts[0];
Diag(First->getLocStart(), diag::note_declared_coroutine_here)
- << (isa<CoawaitExpr>(First) ? 0 :
- isa<CoyieldExpr>(First) ? 1 : 2);
+ << (isa<CoawaitExpr>(First) ? "co_await" :
+ isa<CoyieldExpr>(First) ? "co_yield" : "co_return");
}
SubStmtBuilder Builder(*this, *FD, *Fn, Body);
if (Builder.isInvalid())
@@ -647,40 +707,88 @@ bool SubStmtBuilder::makePromiseStmt() {
return true;
}
-bool SubStmtBuilder::makeInitialSuspend() {
- // Form and check implicit 'co_await p.initial_suspend();' statement.
- ExprResult InitialSuspend =
- buildPromiseCall(S, &Fn, Loc, "initial_suspend", None);
- // FIXME: Support operator co_await here.
- if (!InitialSuspend.isInvalid())
- InitialSuspend = S.BuildCoawaitExpr(Loc, InitialSuspend.get());
- InitialSuspend = S.ActOnFinishFullExpr(InitialSuspend.get());
- if (InitialSuspend.isInvalid())
+bool SubStmtBuilder::makeInitialAndFinalSuspend() {
+ if (Fn.hasInvalidCoroutineSuspends())
return false;
-
- this->InitialSuspend = InitialSuspend.get();
+ this->InitialSuspend = cast<Expr>(Fn.CoroutineSuspends.first);
+ this->FinalSuspend = cast<Expr>(Fn.CoroutineSuspends.second);
return true;
}
-bool SubStmtBuilder::makeFinalSuspend() {
- // Form and check implicit 'co_await p.final_suspend();' statement.
- ExprResult FinalSuspend =
- buildPromiseCall(S, &Fn, Loc, "final_suspend", None);
- // FIXME: Support operator co_await here.
- if (!FinalSuspend.isInvalid())
- FinalSuspend = S.BuildCoawaitExpr(Loc, FinalSuspend.get());
- FinalSuspend = S.ActOnFinishFullExpr(FinalSuspend.get());
- if (FinalSuspend.isInvalid())
+bool SubStmtBuilder::makeNewAndDeleteExpr() {
+ // Form and check allocation and deallocation calls.
+ QualType PromiseType = Fn.CoroutinePromise->getType();
+ if (PromiseType->isDependentType())
+ return true;
+
+ if (S.RequireCompleteType(Loc, PromiseType, diag::err_incomplete_type))
return false;
- this->FinalSuspend = FinalSuspend.get();
- return true;
-}
+ // FIXME: Add support for get_return_object_on_allocation failure.
+ // FIXME: Add support for stateful allocators.
-bool SubStmtBuilder::makeNewAndDeleteExpr() {
- // Form and check allocation and deallocation calls.
- return buildAllocationAndDeallocation(S, Loc, &Fn, this->Allocate,
- this->Deallocate);
+ FunctionDecl *OperatorNew = nullptr;
+ FunctionDecl *OperatorDelete = nullptr;
+ FunctionDecl *UnusedResult = nullptr;
+ bool PassAlignment = false;
+
+ S.FindAllocationFunctions(Loc, SourceRange(),
+ /*UseGlobal*/ false, PromiseType,
+ /*isArray*/ false, PassAlignment,
+ /*PlacementArgs*/ None, OperatorNew, UnusedResult);
+
+ OperatorDelete = findDeleteForPromise(S, Loc, PromiseType);
+
+ if (!OperatorDelete || !OperatorNew)
+ return false;
+
+ Expr *FramePtr =
+ buildBuiltinCall(S, Loc, Builtin::BI__builtin_coro_frame, {});
+
+ Expr *FrameSize =
+ buildBuiltinCall(S, Loc, Builtin::BI__builtin_coro_size, {});
+
+ // Make new call.
+
+ ExprResult NewRef =
+ S.BuildDeclRefExpr(OperatorNew, OperatorNew->getType(), VK_LValue, Loc);
+ if (NewRef.isInvalid())
+ return false;
+
+ ExprResult NewExpr =
+ S.ActOnCallExpr(S.getCurScope(), NewRef.get(), Loc, FrameSize, Loc);
+ if (NewExpr.isInvalid())
+ return false;
+
+ // Make delete call.
+
+ QualType OpDeleteQualType = OperatorDelete->getType();
+
+ ExprResult DeleteRef =
+ S.BuildDeclRefExpr(OperatorDelete, OpDeleteQualType, VK_LValue, Loc);
+ if (DeleteRef.isInvalid())
+ return false;
+
+ Expr *CoroFree =
+ buildBuiltinCall(S, Loc, Builtin::BI__builtin_coro_free, {FramePtr});
+
+ SmallVector<Expr *, 2> DeleteArgs{CoroFree};
+
+ // Check if we need to pass the size.
+ const auto *OpDeleteType =
+ OpDeleteQualType.getTypePtr()->getAs<FunctionProtoType>();
+ if (OpDeleteType->getNumParams() > 1)
+ DeleteArgs.push_back(FrameSize);
+
+ ExprResult DeleteExpr =
+ S.ActOnCallExpr(S.getCurScope(), DeleteRef.get(), Loc, DeleteArgs, Loc);
+ if (DeleteExpr.isInvalid())
+ return false;
+
+ this->Allocate = NewExpr.get();
+ this->Deallocate = DeleteExpr.get();
+
+ return true;
}
bool SubStmtBuilder::makeOnFallthrough() {
@@ -690,13 +798,8 @@ bool SubStmtBuilder::makeOnFallthrough()
// [dcl.fct.def.coroutine]/4
// The unqualified-ids 'return_void' and 'return_value' are looked up in
// the scope of class P. If both are found, the program is ill-formed.
- DeclarationName RVoidDN = S.PP.getIdentifierInfo("return_void");
- LookupResult RVoidResult(S, RVoidDN, Loc, Sema::LookupMemberName);
- const bool HasRVoid = S.LookupQualifiedName(RVoidResult, PromiseRecordDecl);
-
- DeclarationName RValueDN = S.PP.getIdentifierInfo("return_value");
- LookupResult RValueResult(S, RValueDN, Loc, Sema::LookupMemberName);
- const bool HasRValue = S.LookupQualifiedName(RValueResult, PromiseRecordDecl);
+ const bool HasRVoid = lookupMember(S, "return_void", PromiseRecordDecl, Loc);
+ const bool HasRValue = lookupMember(S, "return_value", PromiseRecordDecl, Loc);
StmtResult Fallthrough;
if (HasRVoid && HasRValue) {
@@ -708,7 +811,8 @@ bool SubStmtBuilder::makeOnFallthrough()
// If the unqualified-id return_void is found, flowing off the end of a
// coroutine is equivalent to a co_return with no operand. Otherwise,
// flowing off the end of a coroutine results in undefined behavior.
- Fallthrough = S.BuildCoreturnStmt(FD.getLocation(), nullptr);
+ Fallthrough = S.BuildCoreturnStmt(FD.getLocation(), nullptr,
+ /*IsImplicit*/false);
Fallthrough = S.ActOnFinishFullStmt(Fallthrough.get());
if (Fallthrough.isInvalid())
return false;
@@ -736,15 +840,13 @@ bool SubStmtBuilder::makeOnException() {
// [dcl.fct.def.coroutine]/3
// The unqualified-id set_exception is found in the scope of P by class
// member access lookup (3.4.5).
- DeclarationName SetExDN = S.PP.getIdentifierInfo("set_exception");
- LookupResult SetExResult(S, SetExDN, Loc, Sema::LookupMemberName);
- if (S.LookupQualifiedName(SetExResult, PromiseRecordDecl)) {
+ if (lookupMember(S, "set_exception", PromiseRecordDecl, Loc)) {
// Form the call 'p.set_exception(std::current_exception())'
SetException = buildStdCurrentExceptionCall(S, Loc);
if (SetException.isInvalid())
return false;
Expr *E = SetException.get();
- SetException = buildPromiseCall(S, &Fn, Loc, "set_exception", E);
+ SetException = buildPromiseCall(S, Fn.CoroutinePromise, Loc, "set_exception", E);
SetException = S.ActOnFinishFullExpr(SetException.get(), Loc);
if (SetException.isInvalid())
return false;
@@ -759,7 +861,7 @@ bool SubStmtBuilder::makeReturnObject()
// Build implicit 'p.get_return_object()' expression and form initialization
// of return type from it.
ExprResult ReturnObject =
- buildPromiseCall(S, &Fn, Loc, "get_return_object", None);
+ buildPromiseCall(S, Fn.CoroutinePromise, Loc, "get_return_object", None);
if (ReturnObject.isInvalid())
return false;
QualType RetType = FD.getReturnType();
@@ -783,3 +885,10 @@ bool SubStmtBuilder::makeParamMoves() {
// FIXME: Perform move-initialization of parameters into frame-local copies.
return true;
}
+
+StmtResult Sema::BuildCoroutineBodyStmt(CoroutineBodyStmt::CtorArgs Args) {
+ CoroutineBodyStmt *Res = CoroutineBodyStmt::Create(Context, Args);
+ if (!Res)
+ return StmtError();
+ return Res;
+}
Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=297093&r1=297092&r2=297093&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Mon Mar 6 17:38:15 2017
@@ -11989,7 +11989,7 @@ Decl *Sema::ActOnFinishFunctionBody(Decl
sema::AnalysisBasedWarnings::Policy WP = AnalysisWarnings.getDefaultPolicy();
sema::AnalysisBasedWarnings::Policy *ActivePolicy = nullptr;
- if (getLangOpts().CoroutinesTS && !getCurFunction()->CoroutineStmts.empty())
+ if (getLangOpts().CoroutinesTS && getCurFunction()->CoroutinePromise)
CheckCompletedCoroutineBody(FD, Body);
if (FD) {
Modified: cfe/trunk/lib/Sema/SemaExceptionSpec.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExceptionSpec.cpp?rev=297093&r1=297092&r2=297093&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExceptionSpec.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExceptionSpec.cpp Mon Mar 6 17:38:15 2017
@@ -1182,6 +1182,7 @@ CanThrowResult Sema::canThrow(const Expr
case Expr::ArraySubscriptExprClass:
case Expr::OMPArraySectionExprClass:
case Expr::BinaryOperatorClass:
+ case Expr::DependentCoawaitExprClass:
case Expr::CompoundAssignOperatorClass:
case Expr::CStyleCastExprClass:
case Expr::CXXStaticCastExprClass:
Modified: cfe/trunk/lib/Sema/TreeTransform.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/TreeTransform.h?rev=297093&r1=297092&r2=297093&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/TreeTransform.h (original)
+++ cfe/trunk/lib/Sema/TreeTransform.h Mon Mar 6 17:38:15 2017
@@ -1362,16 +1362,28 @@ public:
///
/// By default, performs semantic analysis to build the new statement.
/// Subclasses may override this routine to provide different behavior.
- StmtResult RebuildCoreturnStmt(SourceLocation CoreturnLoc, Expr *Result) {
- return getSema().BuildCoreturnStmt(CoreturnLoc, Result);
+ StmtResult RebuildCoreturnStmt(SourceLocation CoreturnLoc, Expr *Result,
+ bool IsImplicit) {
+ return getSema().BuildCoreturnStmt(CoreturnLoc, Result, IsImplicit);
}
/// \brief Build a new co_await expression.
///
/// By default, performs semantic analysis to build the new expression.
/// Subclasses may override this routine to provide different behavior.
- ExprResult RebuildCoawaitExpr(SourceLocation CoawaitLoc, Expr *Result) {
- return getSema().BuildCoawaitExpr(CoawaitLoc, Result);
+ ExprResult RebuildCoawaitExpr(SourceLocation CoawaitLoc, Expr *Result,
+ bool IsImplicit) {
+ return getSema().BuildResolvedCoawaitExpr(CoawaitLoc, Result, IsImplicit);
+ }
+
+ /// \brief Build a new co_await expression.
+ ///
+ /// By default, performs semantic analysis to build the new expression.
+ /// Subclasses may override this routine to provide different behavior.
+ ExprResult RebuildDependentCoawaitExpr(SourceLocation CoawaitLoc,
+ Expr *Result,
+ UnresolvedLookupExpr *Lookup) {
+ return getSema().BuildUnresolvedCoawaitExpr(CoawaitLoc, Result, Lookup);
}
/// \brief Build a new co_yield expression.
@@ -1382,6 +1394,10 @@ public:
return getSema().BuildCoyieldExpr(CoyieldLoc, Result);
}
+ StmtResult RebuildCoroutineBodyStmt(CoroutineBodyStmt::CtorArgs Args) {
+ return getSema().BuildCoroutineBodyStmt(Args);
+ }
+
/// \brief Build a new Objective-C \@try statement.
///
/// By default, performs semantic analysis to build the new statement.
@@ -6833,7 +6849,91 @@ StmtResult
TreeTransform<Derived>::TransformCoroutineBodyStmt(CoroutineBodyStmt *S) {
// The coroutine body should be re-formed by the caller if necessary.
// FIXME: The coroutine body is always rebuilt by ActOnFinishFunctionBody
- return getDerived().TransformStmt(S->getBody());
+ CoroutineBodyStmt::CtorArgs BodyArgs;
+
+ auto *ScopeInfo = SemaRef.getCurFunction();
+ auto *FD = cast<FunctionDecl>(SemaRef.CurContext);
+ assert(ScopeInfo && !ScopeInfo->CoroutinePromise &&
+ ScopeInfo->NeedsCoroutineSuspends &&
+ ScopeInfo->CoroutineSuspends.first == nullptr &&
+ ScopeInfo->CoroutineSuspends.second == nullptr &&
+ ScopeInfo->CoroutineStmts.empty() && "expected clean scope info");
+
+ // Set that we have (possibly-invalid) suspend points before we do anything
+ // that may fail.
+ ScopeInfo->setNeedsCoroutineSuspends(false);
+
+ // The new CoroutinePromise object needs to be built and put into the current
+ // FunctionScopeInfo before any transformations or rebuilding occurs.
+ auto *Promise = S->getPromiseDecl();
+ auto *NewPromise = SemaRef.buildCoroutinePromise(FD->getLocation());
+ if (!NewPromise)
+ return StmtError();
+ getDerived().transformedLocalDecl(Promise, NewPromise);
+ ScopeInfo->CoroutinePromise = NewPromise;
+ StmtResult PromiseStmt = SemaRef.ActOnDeclStmt(
+ SemaRef.ConvertDeclToDeclGroup(NewPromise),
+ FD->getLocation(), FD->getLocation());
+ assert(!PromiseStmt.isInvalid());
+ BodyArgs.Promise = PromiseStmt.get();
+
+ // Transform the implicit coroutine statements we built during the initial
+ // parse.
+ StmtResult InitSuspend = getDerived().TransformStmt(S->getInitSuspendStmt());
+ if (InitSuspend.isInvalid())
+ return StmtError();
+ StmtResult FinalSuspend =
+ getDerived().TransformStmt(S->getFinalSuspendStmt());
+ if (FinalSuspend.isInvalid())
+ return StmtError();
+ ScopeInfo->setCoroutineSuspends(InitSuspend.get(), FinalSuspend.get());
+ assert(isa<Expr>(InitSuspend.get()) && isa<Expr>(FinalSuspend.get()));
+ BodyArgs.InitialSuspend = cast<Expr>(InitSuspend.get());
+ BodyArgs.FinalSuspend = cast<Expr>(FinalSuspend.get());
+
+ StmtResult BodyRes = getDerived().TransformStmt(S->getBody());
+ if (BodyRes.isInvalid())
+ return StmtError();
+ BodyArgs.Body = BodyRes.get();
+
+ if (S->getFallthroughHandler()) {
+ StmtResult Res = getDerived().TransformStmt(S->getFallthroughHandler());
+ if (Res.isInvalid())
+ return StmtError();
+ BodyArgs.OnFallthrough = Res.get();
+ }
+
+ if (S->getExceptionHandler()) {
+ StmtResult Res = getDerived().TransformStmt(S->getExceptionHandler());
+ if (Res.isInvalid())
+ return StmtError();
+ BodyArgs.OnException = Res.get();
+ }
+
+ // Transform any additional statements we may have already built
+ if (S->getAllocate() && S->getDeallocate()) {
+ ExprResult AllocRes = getDerived().TransformExpr(S->getAllocate());
+ if (AllocRes.isInvalid())
+ return StmtError();
+ BodyArgs.Allocate = AllocRes.get();
+
+ ExprResult DeallocRes = getDerived().TransformExpr(S->getDeallocate());
+ if (DeallocRes.isInvalid())
+ return StmtError();
+ BodyArgs.Deallocate = DeallocRes.get();
+ }
+
+ Expr *ReturnObject = S->getReturnValueInit();
+ if (ReturnObject) {
+ ExprResult Res = getDerived().TransformInitializer(ReturnObject,
+ /*NoCopyInit*/false);
+ if (Res.isInvalid())
+ return StmtError();
+ BodyArgs.ReturnValue = Res.get();
+ }
+
+ // Do a partial rebuild of the coroutine body and stash it in the ScopeInfo
+ return getDerived().RebuildCoroutineBodyStmt(BodyArgs);
}
template<typename Derived>
@@ -6846,7 +6946,8 @@ TreeTransform<Derived>::TransformCoretur
// Always rebuild; we don't know if this needs to be injected into a new
// context or if the promise type has changed.
- return getDerived().RebuildCoreturnStmt(S->getKeywordLoc(), Result.get());
+ return getDerived().RebuildCoreturnStmt(S->getKeywordLoc(), Result.get(),
+ S->isImplicit());
}
template<typename Derived>
@@ -6859,7 +6960,29 @@ TreeTransform<Derived>::TransformCoawait
// Always rebuild; we don't know if this needs to be injected into a new
// context or if the promise type has changed.
- return getDerived().RebuildCoawaitExpr(E->getKeywordLoc(), Result.get());
+ return getDerived().RebuildCoawaitExpr(E->getKeywordLoc(), Result.get(),
+ E->isImplicit());
+}
+
+template <typename Derived>
+ExprResult
+TreeTransform<Derived>::TransformDependentCoawaitExpr(DependentCoawaitExpr *E) {
+ ExprResult OperandResult = getDerived().TransformInitializer(E->getOperand(),
+ /*NotCopyInit*/ false);
+ if (OperandResult.isInvalid())
+ return ExprError();
+
+ ExprResult LookupResult = getDerived().TransformUnresolvedLookupExpr(
+ E->getOperatorCoawaitLookup());
+
+ if (LookupResult.isInvalid())
+ return ExprError();
+
+ // Always rebuild; we don't know if this needs to be injected into a new
+ // context or if the promise type has changed.
+ return getDerived().RebuildDependentCoawaitExpr(
+ E->getKeywordLoc(), OperandResult.get(),
+ cast<UnresolvedLookupExpr>(LookupResult.get()));
}
template<typename Derived>
Modified: cfe/trunk/lib/Serialization/ASTReaderStmt.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReaderStmt.cpp?rev=297093&r1=297092&r2=297093&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTReaderStmt.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTReaderStmt.cpp Mon Mar 6 17:38:15 2017
@@ -381,6 +381,11 @@ void ASTStmtReader::VisitCoawaitExpr(Coa
llvm_unreachable("unimplemented");
}
+void ASTStmtReader::VisitDependentCoawaitExpr(DependentCoawaitExpr *S) {
+ // FIXME: Implement coroutine serialization.
+ llvm_unreachable("unimplemented");
+}
+
void ASTStmtReader::VisitCoyieldExpr(CoyieldExpr *S) {
// FIXME: Implement coroutine serialization.
llvm_unreachable("unimplemented");
Modified: cfe/trunk/lib/Serialization/ASTWriterStmt.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTWriterStmt.cpp?rev=297093&r1=297092&r2=297093&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTWriterStmt.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTWriterStmt.cpp Mon Mar 6 17:38:15 2017
@@ -315,6 +315,11 @@ void ASTStmtWriter::VisitCoawaitExpr(Coa
llvm_unreachable("unimplemented");
}
+void ASTStmtWriter::VisitDependentCoawaitExpr(DependentCoawaitExpr *S) {
+ // FIXME: Implement coroutine serialization.
+ llvm_unreachable("unimplemented");
+}
+
void ASTStmtWriter::VisitCoyieldExpr(CoyieldExpr *S) {
// FIXME: Implement coroutine serialization.
llvm_unreachable("unimplemented");
Modified: cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp?rev=297093&r1=297092&r2=297093&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp Mon Mar 6 17:38:15 2017
@@ -800,6 +800,7 @@ void ExprEngine::Visit(const Stmt *S, Ex
case Stmt::FunctionParmPackExprClass:
case Stmt::CoroutineBodyStmtClass:
case Stmt::CoawaitExprClass:
+ case Stmt::DependentCoawaitExprClass:
case Stmt::CoreturnStmtClass:
case Stmt::CoyieldExprClass:
case Stmt::SEHTryStmtClass:
Modified: cfe/trunk/test/SemaCXX/coroutines.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/coroutines.cpp?rev=297093&r1=297092&r2=297093&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/coroutines.cpp (original)
+++ cfe/trunk/test/SemaCXX/coroutines.cpp Mon Mar 6 17:38:15 2017
@@ -73,7 +73,7 @@ template <>
struct std::experimental::coroutine_traits<double, int> {
struct promise_type {};
};
-double bad_promise_type_2(int) {
+double bad_promise_type_2(int) { // expected-error {{no member named 'initial_suspend'}}
co_yield 0; // expected-error {{no member named 'yield_value' in 'std::experimental::coroutine_traits<double, int>::promise_type'}}
}
@@ -93,6 +93,7 @@ struct coroutine_handle;
}
}
+// FIXME: This diagnostic is terrible.
void undefined_promise() { // expected-error {{this function cannot be a coroutine: 'experimental::coroutine_traits<void>::promise_type' (aka 'promise') is an incomplete type}}
co_await a;
}
@@ -213,6 +214,13 @@ auto deduced_return_coroutine() {
}
struct outer {};
+struct await_arg_1 {};
+struct await_arg_2 {};
+
+namespace adl_ns {
+struct coawait_arg_type {};
+awaitable operator co_await(coawait_arg_type);
+}
namespace dependent_operator_co_await_lookup {
template<typename T> void await_template(T t) {
@@ -235,6 +243,94 @@ namespace dependent_operator_co_await_lo
};
template void await_template(outer); // expected-note {{instantiation}}
template void await_template_2(outer);
+
+ struct transform_awaitable {};
+ struct transformed {};
+
+ struct transform_promise {
+ typedef transform_awaitable await_arg;
+ coro<transform_promise> get_return_object();
+ transformed initial_suspend();
+ ::adl_ns::coawait_arg_type final_suspend();
+ transformed await_transform(transform_awaitable);
+ };
+ template <class AwaitArg>
+ struct basic_promise {
+ typedef AwaitArg await_arg;
+ coro<basic_promise> get_return_object();
+ awaitable initial_suspend();
+ awaitable final_suspend();
+ };
+
+ awaitable operator co_await(await_arg_1);
+
+ template <typename T, typename U>
+ coro<T> await_template_3(U t) {
+ co_await t;
+ }
+
+ template coro<basic_promise<await_arg_1>> await_template_3<basic_promise<await_arg_1>>(await_arg_1);
+
+ template <class T, int I = 0>
+ struct dependent_member {
+ coro<T> mem_fn() const {
+ co_await typename T::await_arg{}; // expected-error {{call to function 'operator co_await'}}}
+ }
+ template <class U>
+ coro<T> dep_mem_fn(U t) {
+ co_await t;
+ }
+ };
+
+ template <>
+ struct dependent_member<long> {
+ // FIXME this diagnostic is terrible
+ coro<transform_promise> mem_fn() const { // expected-error {{no member named 'await_ready' in 'dependent_operator_co_await_lookup::transformed'}}
+ // expected-note at -1 {{call to 'initial_suspend' implicitly required by the initial suspend point}}
+ // expected-note at +1 {{function is a coroutine due to use of 'co_await' here}}
+ co_await transform_awaitable{};
+ // expected-error at -1 {{no member named 'await_ready'}}
+ }
+ template <class R, class U>
+ coro<R> dep_mem_fn(U u) { co_await u; }
+ };
+
+ awaitable operator co_await(await_arg_2); // expected-note {{'operator co_await' should be declared prior to the call site}}
+
+ template struct dependent_member<basic_promise<await_arg_1>, 0>;
+ template struct dependent_member<basic_promise<await_arg_2>, 0>; // expected-note {{in instantiation}}
+
+ template <>
+ coro<transform_promise>
+ // FIXME this diagnostic is terrible
+ dependent_member<long>::dep_mem_fn<transform_promise>(int) { // expected-error {{no member named 'await_ready' in 'dependent_operator_co_await_lookup::transformed'}}
+ //expected-note at -1 {{call to 'initial_suspend' implicitly required by the initial suspend point}}
+ //expected-note at +1 {{function is a coroutine due to use of 'co_await' here}}
+ co_await transform_awaitable{};
+ // expected-error at -1 {{no member named 'await_ready'}}
+ }
+
+ void operator co_await(transform_awaitable) = delete;
+ awaitable operator co_await(transformed);
+
+ template coro<transform_promise>
+ dependent_member<long>::dep_mem_fn<transform_promise>(transform_awaitable);
+
+ template <>
+ coro<transform_promise> dependent_member<long>::dep_mem_fn<transform_promise>(long) {
+ co_await transform_awaitable{};
+ }
+
+ template <>
+ struct dependent_member<int> {
+ coro<transform_promise> mem_fn() const {
+ co_await transform_awaitable{};
+ }
+ };
+
+ template coro<transform_promise> await_template_3<transform_promise>(transform_awaitable);
+ template struct dependent_member<transform_promise>;
+ template coro<transform_promise> dependent_member<transform_promise>::dep_mem_fn(transform_awaitable);
}
struct yield_fn_tag {};
@@ -290,6 +386,7 @@ struct bad_promise_2 {
// FIXME: We shouldn't offer a typo-correction here!
suspend_always final_suspend(); // expected-note {{here}}
};
+// FIXME: This shouldn't happen twice
coro<bad_promise_2> missing_initial_suspend() { // expected-error {{no member named 'initial_suspend' in 'bad_promise_2'}}
co_await a;
}
@@ -310,7 +407,8 @@ struct bad_promise_4 {
};
// FIXME: This diagnostic is terrible.
coro<bad_promise_4> bad_initial_suspend() { // expected-error {{no member named 'await_ready' in 'not_awaitable'}}
- co_await a;
+ // expected-note at -1 {{call to 'initial_suspend' implicitly required by the initial suspend point}}
+ co_await a; // expected-note {{function is a coroutine due to use of 'co_await' here}}
}
struct bad_promise_5 {
@@ -320,7 +418,8 @@ struct bad_promise_5 {
};
// FIXME: This diagnostic is terrible.
coro<bad_promise_5> bad_final_suspend() { // expected-error {{no member named 'await_ready' in 'not_awaitable'}}
- co_await a;
+ // expected-note at -1 {{call to 'final_suspend' implicitly required by the final suspend point}}
+ co_await a; // expected-note {{function is a coroutine due to use of 'co_await' here}}
}
struct bad_promise_6 {
@@ -351,20 +450,70 @@ namespace std {
int *current_exception();
}
-struct bad_promise_8 {
+struct bad_promise_base {
+private:
+ void return_void();
+};
+struct bad_promise_8 : bad_promise_base {
coro<bad_promise_8> get_return_object();
suspend_always initial_suspend();
suspend_always final_suspend();
- void return_void();
void set_exception(); // expected-note {{function not viable}}
void set_exception(int *) __attribute__((unavailable)); // expected-note {{explicitly made unavailable}}
void set_exception(void *); // expected-note {{candidate function}}
};
coro<bad_promise_8> calls_set_exception() {
// expected-error at -1 {{call to unavailable member function 'set_exception'}}
+ // FIXME: also warn about private 'return_void' here. Even though building
+ // the call to set_exception has already failed.
co_await a;
}
+struct bad_promise_9 {
+ coro<bad_promise_9> get_return_object();
+ suspend_always initial_suspend();
+ suspend_always final_suspend();
+ void await_transform(void *); // expected-note {{candidate}}
+ awaitable await_transform(int) __attribute__((unavailable)); // expected-note {{explicitly made unavailable}}
+ void return_void();
+};
+coro<bad_promise_9> calls_await_transform() {
+ co_await 42; // expected-error {{call to unavailable member function 'await_transform'}}
+ // expected-note at -1 {{call to 'await_transform' implicitly required by 'co_await' here}}
+}
+
+struct bad_promise_10 {
+ coro<bad_promise_10> get_return_object();
+ suspend_always initial_suspend();
+ suspend_always final_suspend();
+ int await_transform;
+ void return_void();
+};
+coro<bad_promise_10> bad_coawait() {
+ // FIXME this diagnostic is terrible
+ co_await 42; // expected-error {{called object type 'int' is not a function or function pointer}}
+ // expected-note at -1 {{call to 'await_transform' implicitly required by 'co_await' here}}
+}
+
+struct call_operator {
+ template <class... Args>
+ awaitable operator()(Args...) const { return a; }
+};
+void ret_void();
+struct good_promise_1 {
+ coro<good_promise_1> get_return_object();
+ suspend_always initial_suspend();
+ suspend_always final_suspend();
+ static const call_operator await_transform;
+ using Fn = void (*)();
+ Fn return_void = ret_void;
+};
+const call_operator good_promise_1::await_transform;
+coro<good_promise_1> ok_static_coawait() {
+ // FIXME this diagnostic is terrible
+ co_await 42;
+}
+
template<> struct std::experimental::coroutine_traits<int, int, const char**>
{ using promise_type = promise; };
Modified: cfe/trunk/tools/libclang/CXCursor.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/libclang/CXCursor.cpp?rev=297093&r1=297092&r2=297093&view=diff
==============================================================================
--- cfe/trunk/tools/libclang/CXCursor.cpp (original)
+++ cfe/trunk/tools/libclang/CXCursor.cpp Mon Mar 6 17:38:15 2017
@@ -231,6 +231,7 @@ CXCursor cxcursor::MakeCXCursor(const St
case Stmt::TypeTraitExprClass:
case Stmt::CoroutineBodyStmtClass:
case Stmt::CoawaitExprClass:
+ case Stmt::DependentCoawaitExprClass:
case Stmt::CoreturnStmtClass:
case Stmt::CoyieldExprClass:
case Stmt::CXXBindTemporaryExprClass:
More information about the cfe-commits
mailing list