[coroutines] Creation of promise object, lookup of operator co_await, building

of await_* calls, and AST representation for same.

llvm-svn: 251387
This commit is contained in:
Richard Smith 2015-10-27 06:02:45 +00:00
parent d5510d1e5c
commit 9f690bd80b
36 changed files with 871 additions and 132 deletions

View File

@ -43,7 +43,7 @@
OPERATOR(PostInc) OPERATOR(PostDec) OPERATOR(PreInc) OPERATOR(PreDec) \
OPERATOR(AddrOf) OPERATOR(Deref) OPERATOR(Plus) OPERATOR(Minus) \
OPERATOR(Not) OPERATOR(LNot) OPERATOR(Real) OPERATOR(Imag) \
OPERATOR(Extension)
OPERATOR(Extension) OPERATOR(Coawait)
// All binary operators (excluding compound assign operators).
#define BINOP_LIST() \
@ -2306,6 +2306,12 @@ DEF_TRAVERSE_STMT(MaterializeTemporaryExpr, {})
DEF_TRAVERSE_STMT(CXXFoldExpr, {})
DEF_TRAVERSE_STMT(AtomicExpr, {})
// Coroutine support.
DEF_TRAVERSE_STMT(CoroutineBodyStmt, {})
DEF_TRAVERSE_STMT(CoreturnStmt, {})
DEF_TRAVERSE_STMT(CoawaitExpr, {})
DEF_TRAVERSE_STMT(CoyieldExpr, {})
// These literals (all of them) do not need any action.
DEF_TRAVERSE_STMT(IntegerLiteral, {})
DEF_TRAVERSE_STMT(CharacterLiteral, {})

View File

@ -4008,6 +4008,129 @@ public:
child_range children() { return child_range(SubExprs, SubExprs + 2); }
};
/// \brief Represents a 'co_await' expression. This expression checks whether its
/// operand is ready, and suspends the coroutine if not. Then (after the resume
/// if suspended) it resumes the coroutine and extracts the value from the
/// operand. This implies making four calls:
///
/// <operand>.operator co_await() or operator co_await(<operand>)
/// <result>.await_ready()
/// <result>.await_suspend(h)
/// <result>.await_resume()
///
/// where h is a handle to the coroutine, and <result> is the result of calling
/// operator co_await() if it exists or the original operand otherwise.
///
/// Note that the coroutine is prepared for suspension before the 'await_suspend'
/// call, but resumes after that call, which may cause parts of the
/// 'await_suspend' expression to occur much later than expected.
class CoawaitExpr : public Expr {
SourceLocation CoawaitLoc;
enum SubExpr { Operand, Ready, Suspend, Resume, Count };
Stmt *SubExprs[SubExpr::Count];
friend class ASTStmtReader;
public:
CoawaitExpr(SourceLocation CoawaitLoc, Expr *Operand, Expr *Ready,
Expr *Suspend, Expr *Resume)
: Expr(CoawaitExprClass, Resume->getType(), Resume->getValueKind(),
Resume->getObjectKind(),
Resume->isTypeDependent(),
Resume->isValueDependent(),
Operand->isInstantiationDependent(),
Operand->containsUnexpandedParameterPack()),
CoawaitLoc(CoawaitLoc),
SubExprs{Operand, Ready, Suspend, Resume} {}
CoawaitExpr(SourceLocation CoawaitLoc, QualType Ty, Expr *Operand)
: Expr(CoawaitExprClass, Ty, VK_RValue, OK_Ordinary,
true, true, true, Operand->containsUnexpandedParameterPack()),
CoawaitLoc(CoawaitLoc), SubExprs{Operand} {
assert(Operand->isTypeDependent() && Ty->isDependentType() &&
"wrong constructor for non-dependent co_await expression");
}
CoawaitExpr(EmptyShell Empty) : Expr(CoawaitExprClass, Empty) {}
SourceLocation getKeywordLoc() const { return CoawaitLoc; }
Expr *getOperand() const {
return static_cast<Expr*>(SubExprs[SubExpr::Operand]);
}
Expr *getReadyExpr() const {
return static_cast<Expr*>(SubExprs[SubExpr::Ready]);
}
Expr *getSuspendExpr() const {
return static_cast<Expr*>(SubExprs[SubExpr::Suspend]);
}
Expr *getResumeExpr() const {
return static_cast<Expr*>(SubExprs[SubExpr::Resume]);
}
SourceLocation getLocStart() const LLVM_READONLY {
return CoawaitLoc;
}
SourceLocation getLocEnd() const LLVM_READONLY {
return getOperand()->getLocEnd();
}
child_range children() {
return child_range(SubExprs, SubExprs + SubExpr::Count);
}
static bool classof(const Stmt *T) {
return T->getStmtClass() == CoawaitExprClass;
}
};
/// \brief Represents a 'co_yield' expression. This expression provides a value
/// to the coroutine promise and optionally suspends the coroutine. This implies
/// a making call to <promise>.yield_value(<operand>), which we name the "promise
/// call".
class CoyieldExpr : public Expr {
SourceLocation CoyieldLoc;
/// The operand of the 'co_yield' expression.
Stmt *Operand;
/// The implied call to the promise object. May be null if the
/// coroutine has not yet been finalized.
Stmt *PromiseCall;
friend class ASTStmtReader;
public:
CoyieldExpr(SourceLocation CoyieldLoc, QualType Void, Expr *Operand)
: Expr(CoyieldExprClass, Void, VK_RValue, OK_Ordinary, false, false,
Operand->isInstantiationDependent(),
Operand->containsUnexpandedParameterPack()),
CoyieldLoc(CoyieldLoc), Operand(Operand), PromiseCall(nullptr) {}
CoyieldExpr(EmptyShell Empty) : Expr(CoyieldExprClass, Empty) {}
SourceLocation getKeywordLoc() const { return CoyieldLoc; }
Expr *getOperand() const { return static_cast<Expr*>(Operand); }
/// \brief Get the call to the promise objet that is implied by an evaluation
/// of this expression. Will be nullptr if the coroutine has not yet been
/// finalized.
Expr *getPromiseCall() const { return static_cast<Expr*>(PromiseCall); }
/// \brief Set the resolved promise call. This is delayed until the
/// complete coroutine body has been parsed and the promise type is known.
void finalize(Stmt *PC) { PromiseCall = PC; }
SourceLocation getLocStart() const LLVM_READONLY { return CoyieldLoc; }
SourceLocation getLocEnd() const LLVM_READONLY {
return Operand->getLocEnd();
}
child_range children() {
Stmt **Which = PromiseCall ? &PromiseCall : &Operand;
return child_range(Which, Which + 1);
}
static bool classof(const Stmt *T) {
return T->getStmtClass() == CoyieldExprClass;
}
};
} // end namespace clang
#endif

View File

@ -334,7 +334,8 @@ enum UnaryOperatorKind {
UO_Plus, UO_Minus, // [C99 6.5.3.3] Unary arithmetic
UO_Not, UO_LNot, // [C99 6.5.3.3] Unary arithmetic
UO_Real, UO_Imag, // "__real expr"/"__imag expr" Extension.
UO_Extension // __extension__ marker.
UO_Extension, // __extension__ marker.
UO_Coawait // [C++ Coroutines] co_await operator
};
/// \brief The kind of bridging performed by the Objective-C bridge cast.

View File

@ -43,7 +43,7 @@
OPERATOR(PostInc) OPERATOR(PostDec) OPERATOR(PreInc) OPERATOR(PreDec) \
OPERATOR(AddrOf) OPERATOR(Deref) OPERATOR(Plus) OPERATOR(Minus) \
OPERATOR(Not) OPERATOR(LNot) OPERATOR(Real) OPERATOR(Imag) \
OPERATOR(Extension)
OPERATOR(Extension) OPERATOR(Coawait)
// All binary operators (excluding compound assign operators).
#define BINOP_LIST() \
@ -2346,6 +2346,34 @@ DEF_TRAVERSE_STMT(MaterializeTemporaryExpr, {})
DEF_TRAVERSE_STMT(CXXFoldExpr, {})
DEF_TRAVERSE_STMT(AtomicExpr, {})
// For coroutines expressions, traverse either the operand
// as written or the implied calls, depending on what the
// derived class requests.
DEF_TRAVERSE_STMT(CoroutineBodyStmt, {
if (!getDerived().shouldVisitImplicitCode()) {
TRY_TO(TraverseStmt(S->getBody()));
return true;
}
})
DEF_TRAVERSE_STMT(CoreturnStmt, {
if (!getDerived().shouldVisitImplicitCode()) {
TRY_TO(TraverseStmt(S->getOperand()));
return true;
}
})
DEF_TRAVERSE_STMT(CoawaitExpr, {
if (!getDerived().shouldVisitImplicitCode()) {
TRY_TO(TraverseStmt(S->getOperand()));
return true;
}
})
DEF_TRAVERSE_STMT(CoyieldExpr, {
if (!getDerived().shouldVisitImplicitCode()) {
TRY_TO(TraverseStmt(S->getOperand()));
return true;
}
})
// These literals (all of them) do not need any action.
DEF_TRAVERSE_STMT(IntegerLiteral, {})
DEF_TRAVERSE_STMT(CharacterLiteral, {})

View File

@ -131,12 +131,16 @@ class CXXForRangeStmt : public Stmt {
// SubExprs[RANGE] is an expression or declstmt.
// SubExprs[COND] and SubExprs[INC] are expressions.
Stmt *SubExprs[END];
SourceLocation CoawaitLoc;
SourceLocation ColonLoc;
SourceLocation RParenLoc;
friend class ASTStmtReader;
public:
CXXForRangeStmt(DeclStmt *Range, DeclStmt *BeginEnd,
Expr *Cond, Expr *Inc, DeclStmt *LoopVar, Stmt *Body,
SourceLocation FL, SourceLocation CL, SourceLocation RPL);
SourceLocation FL, SourceLocation CAL, SourceLocation CL,
SourceLocation RPL);
CXXForRangeStmt(EmptyShell Empty) : Stmt(CXXForRangeStmtClass, Empty) { }
@ -181,13 +185,10 @@ public:
void setLoopVarStmt(Stmt *S) { SubExprs[LOOPVAR] = S; }
void setBody(Stmt *S) { SubExprs[BODY] = S; }
SourceLocation getForLoc() const { return ForLoc; }
void setForLoc(SourceLocation Loc) { ForLoc = Loc; }
SourceLocation getCoawaitLoc() const { return CoawaitLoc; }
SourceLocation getColonLoc() const { return ColonLoc; }
void setColonLoc(SourceLocation Loc) { ColonLoc = Loc; }
SourceLocation getRParenLoc() const { return RParenLoc; }
void setRParenLoc(SourceLocation Loc) { RParenLoc = Loc; }
SourceLocation getLocStart() const LLVM_READONLY { return ForLoc; }
SourceLocation getLocEnd() const LLVM_READONLY {
@ -287,6 +288,95 @@ public:
}
};
/// \brief Represents the body of a coroutine. This wraps the normal function
/// body and holds the additional semantic context required to set up and tear
/// down the coroutine frame.
class CoroutineBodyStmt : public Stmt {
enum SubStmt { Body, Count };
Stmt *SubStmts[SubStmt::Count];
friend class ASTStmtReader;
public:
CoroutineBodyStmt(Stmt *Body)
: Stmt(CoroutineBodyStmtClass), SubStmts{Body} {}
/// \brief Retrieve the body of the coroutine as written. This will be either
/// a CompoundStmt or a TryStmt.
Stmt *getBody() const {
return SubStmts[SubStmt::Body];
}
SourceLocation getLocStart() const LLVM_READONLY {
return getBody()->getLocStart();
}
SourceLocation getLocEnd() const LLVM_READONLY {
return getBody()->getLocEnd();
}
child_range children() {
return child_range(SubStmts, SubStmts + SubStmt::Count);
}
static bool classof(const Stmt *T) {
return T->getStmtClass() == CoroutineBodyStmtClass;
}
};
/// \brief Represents a 'co_return' statement in the C++ Coroutines TS.
///
/// This statament models the initialization of the coroutine promise
/// (encapsulating the eventual notional return value) from an expression
/// (or braced-init-list).
///
/// This initialization is modeled by a call to one of:
/// <promise>.return_value(<operand>)
/// <promise>.return_void()
/// which we name the "promise call".
class CoreturnStmt : public Stmt {
SourceLocation CoreturnLoc;
/// The operand of the 'co_return' statement.
Stmt *Operand;
/// The implied call to the promise object. May be null if the
/// coroutine has not yet been finalized.
Stmt *PromiseCall;
friend class ASTStmtReader;
public:
CoreturnStmt(SourceLocation CoreturnLoc, Stmt *Operand)
: Stmt(CoreturnStmtClass), CoreturnLoc(CoreturnLoc),
Operand(Operand), PromiseCall(nullptr) {}
SourceLocation getKeywordLoc() const { return CoreturnLoc; }
/// \brief Retrieve the operand of the 'co_return' statement. Will be nullptr
/// if none was specified.
Expr *getOperand() const { return static_cast<Expr*>(Operand); }
/// \brief Retrieve the promise call that results from this 'co_return'
/// statement. Will be nullptr if either the coroutine has not yet been
/// finalized or the coroutine has no eventual return type.
Expr *getPromiseCall() const { return static_cast<Expr*>(PromiseCall); }
/// \brief Set the resolved promise call. This is delayed until the
/// complete coroutine body has been parsed and the promise type is known.
void finalize(Stmt *PC) { PromiseCall = PC; }
SourceLocation getLocStart() const LLVM_READONLY { return CoreturnLoc; }
SourceLocation getLocEnd() const LLVM_READONLY {
return Operand->getLocEnd();
}
child_range children() {
Stmt **Which = PromiseCall ? &PromiseCall : &Operand;
return child_range(Which, Which + 1);
}
static bool classof(const Stmt *T) {
return T->getStmtClass() == CoreturnStmtClass;
}
};
} // end namespace clang
#endif

View File

@ -94,6 +94,7 @@ public:
case UO_Real: DISPATCH(UnaryReal, UnaryOperator);
case UO_Imag: DISPATCH(UnaryImag, UnaryOperator);
case UO_Extension: DISPATCH(UnaryExtension, UnaryOperator);
case UO_Coawait: DISPATCH(UnaryCoawait, UnaryOperator);
}
}
@ -158,7 +159,7 @@ public:
UNARYOP_FALLBACK(Plus) UNARYOP_FALLBACK(Minus)
UNARYOP_FALLBACK(Not) UNARYOP_FALLBACK(LNot)
UNARYOP_FALLBACK(Real) UNARYOP_FALLBACK(Imag)
UNARYOP_FALLBACK(Extension)
UNARYOP_FALLBACK(Extension) UNARYOP_FALLBACK(Coawait)
#undef UNARYOP_FALLBACK
// Base case, ignore it. :)

View File

@ -7839,7 +7839,8 @@ def err_module_import_in_implementation : Error<
}
let CategoryName = "Coroutines Issue" in {
def err_return_in_coroutine : Error<"return statement in coroutine">;
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">;
@ -7853,10 +7854,24 @@ def err_coroutine_constexpr : Error<
"'%0' cannot be used in a constexpr function">;
def err_coroutine_varargs : Error<
"'%0' cannot be used in a varargs function">;
def ext_coroutine_without_coawait_coyield : ExtWarn<
def ext_coroutine_without_co_await_co_yield : ExtWarn<
"'co_return' used in a function "
"that uses neither 'co_await' nor 'co_yield'">,
InGroup<DiagGroup<"coreturn-without-coawait">>;
def err_co_await_no_viable_function : Error<
"invalid co_await operand of type %0; "
"no viable '%1' function %select{|for awaited type %3 }2available">;
def err_implied_std_coroutine_traits_not_found : Error<
"you need to include <coroutine> before defining a coroutine">;
def err_malformed_std_coroutine_traits : Error<
"std::coroutine_traits must be a class template">;
def err_implied_std_coroutine_traits_promise_type_not_found : Error<
"this function cannot be a coroutine: %0 has no member named 'promise_type'">;
def err_implied_std_coroutine_traits_promise_type_not_class : Error<
"this function cannot be a coroutine: %0 is not a class">;
def err_coroutine_traits_missing_specialization : Error<
"this function cannot be a coroutine: missing definition of "
"specialization %0">;
}
let CategoryName = "Documentation Issue" in {

View File

@ -48,6 +48,10 @@ def CXXCatchStmt : Stmt;
def CXXTryStmt : Stmt;
def CXXForRangeStmt : Stmt;
// C++ Coroutines TS statements
def CoroutineBodyStmt : Stmt;
def CoreturnStmt : Stmt;
// Expressions
def Expr : Stmt<1>;
def PredefinedExpr : DStmt<Expr>;
@ -140,6 +144,10 @@ def MaterializeTemporaryExpr : DStmt<Expr>;
def LambdaExpr : DStmt<Expr>;
def CXXFoldExpr : DStmt<Expr>;
// C++ Coroutines TS expressions
def CoawaitExpr : DStmt<Expr>;
def CoyieldExpr : DStmt<Expr>;
// Obj-C Expressions.
def ObjCStringLiteral : DStmt<Expr>;
def ObjCBoxedExpr : DStmt<Expr>;

View File

@ -89,40 +89,43 @@ protected:
public:
/// \brief What kind of scope we are describing.
///
ScopeKind Kind;
ScopeKind Kind : 2;
/// \brief Whether this function contains a VLA, \@try, try, C++
/// initializer, or anything else that can't be jumped past.
bool HasBranchProtectedScope;
bool HasBranchProtectedScope : 1;
/// \brief Whether this function contains any switches or direct gotos.
bool HasBranchIntoScope;
bool HasBranchIntoScope : 1;
/// \brief Whether this function contains any indirect gotos.
bool HasIndirectGoto;
bool HasIndirectGoto : 1;
/// \brief Whether a statement was dropped because it was invalid.
bool HasDroppedStmt;
bool HasDroppedStmt : 1;
/// A flag that is set when parsing a method that must call super's
/// implementation, such as \c -dealloc, \c -finalize, or any method marked
/// with \c __attribute__((objc_requires_super)).
bool ObjCShouldCallSuper;
bool ObjCShouldCallSuper : 1;
/// True when this is a method marked as a designated initializer.
bool ObjCIsDesignatedInit;
bool ObjCIsDesignatedInit : 1;
/// This starts true for a method marked as designated initializer and will
/// be set to false if there is an invocation to a designated initializer of
/// the super class.
bool ObjCWarnForNoDesignatedInitChain;
bool ObjCWarnForNoDesignatedInitChain : 1;
/// True when this is an initializer method not marked as a designated
/// initializer within a class that has at least one initializer marked as a
/// designated initializer.
bool ObjCIsSecondaryInit;
bool ObjCIsSecondaryInit : 1;
/// This starts true for a secondary initializer method and will be set to
/// false if there is an invocation of an initializer on 'self'.
bool ObjCWarnForNoInitDelegation;
bool ObjCWarnForNoInitDelegation : 1;
/// First 'return' statement in the current function.
SourceLocation FirstReturnLoc;
/// First C++ 'try' statement in the current function.
SourceLocation FirstCXXTryLoc;
@ -142,6 +145,9 @@ public:
/// optimization, or if we need to infer a return type.
SmallVector<ReturnStmt*, 4> Returns;
/// \brief The promise object for this coroutine, if any.
VarDecl *CoroutinePromise;
/// \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.

View File

@ -2492,17 +2492,8 @@ public:
FRS_DiagnosticIssued
};
// An enum to represent whether something is dealing with a call to begin()
// or a call to end() in a range-based for loop.
enum BeginEndFunction {
BEF_begin,
BEF_end
};
ForRangeStatus BuildForRangeBeginEndCall(Scope *S, SourceLocation Loc,
ForRangeStatus BuildForRangeBeginEndCall(SourceLocation Loc,
SourceLocation RangeLoc,
VarDecl *Decl,
BeginEndFunction BEF,
const DeclarationNameInfo &NameInfo,
LookupResult &MemberLookup,
OverloadCandidateSet *CandidateSet,
@ -3324,7 +3315,7 @@ public:
BFRK_Check
};
StmtResult ActOnCXXForRangeStmt(SourceLocation ForLoc,
StmtResult ActOnCXXForRangeStmt(Scope *S, SourceLocation ForLoc,
SourceLocation CoawaitLoc,
Stmt *LoopVar,
SourceLocation ColonLoc, Expr *Collection,
@ -7699,10 +7690,14 @@ public:
//===--------------------------------------------------------------------===//
// C++ Coroutines TS
//
ExprResult ActOnCoawaitExpr(SourceLocation KwLoc, Expr *E);
ExprResult ActOnCoyieldExpr(SourceLocation KwLoc, Expr *E);
ExprResult ActOnCoawaitExpr(Scope *S, SourceLocation KwLoc, Expr *E);
ExprResult ActOnCoyieldExpr(Scope *S, SourceLocation KwLoc, Expr *E);
StmtResult ActOnCoreturnStmt(SourceLocation KwLoc, Expr *E);
ExprResult BuildCoawaitExpr(SourceLocation KwLoc, Expr *E);
ExprResult BuildCoyieldExpr(SourceLocation KwLoc, Expr *E);
StmtResult BuildCoreturnStmt(SourceLocation KwLoc, Expr *E);
void CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *Body);
//===--------------------------------------------------------------------===//

View File

@ -4915,13 +4915,14 @@ Stmt *ASTNodeImporter::VisitCXXForRangeStmt(CXXForRangeStmt *S) {
if (!ToBody && S->getBody())
return nullptr;
SourceLocation ToForLoc = Importer.Import(S->getForLoc());
SourceLocation ToCoawaitLoc = Importer.Import(S->getCoawaitLoc());
SourceLocation ToColonLoc = Importer.Import(S->getColonLoc());
SourceLocation ToRParenLoc = Importer.Import(S->getRParenLoc());
return new (Importer.getToContext()) CXXForRangeStmt(ToRange, ToBeginEnd,
ToCond, ToInc,
ToLoopVar, ToBody,
ToForLoc, ToColonLoc,
ToRParenLoc);
ToForLoc, ToCoawaitLoc,
ToColonLoc, ToRParenLoc);
}
Stmt *ASTNodeImporter::VisitObjCForCollectionStmt(ObjCForCollectionStmt *S) {

View File

@ -1079,6 +1079,7 @@ StringRef UnaryOperator::getOpcodeStr(Opcode Op) {
case UO_Real: return "__real";
case UO_Imag: return "__imag";
case UO_Extension: return "__extension__";
case UO_Coawait: return "co_await";
}
llvm_unreachable("Unknown unary operator");
}
@ -1095,6 +1096,7 @@ UnaryOperator::getOverloadedOpcode(OverloadedOperatorKind OO, bool Postfix) {
case OO_Minus: return UO_Minus;
case OO_Tilde: return UO_Not;
case OO_Exclaim: return UO_LNot;
case OO_Coawait: return UO_Coawait;
}
}
@ -1108,6 +1110,7 @@ OverloadedOperatorKind UnaryOperator::getOverloadedOperator(Opcode Opc) {
case UO_Minus: return OO_Minus;
case UO_Not: return OO_Tilde;
case UO_LNot: return OO_Exclaim;
case UO_Coawait: return OO_Coawait;
default: return OO_None;
}
}
@ -2050,6 +2053,9 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc,
case UO_LNot:
case UO_Deref:
break;
case UO_Coawait:
// This is just the 'operator co_await' call inside the guts of a
// dependent co_await call.
case UO_PostInc:
case UO_PostDec:
case UO_PreInc:
@ -3005,6 +3011,8 @@ bool Expr::HasSideEffects(const ASTContext &Ctx,
case CXXNewExprClass:
case CXXDeleteExprClass:
case ExprWithCleanupsClass:
case CoawaitExprClass:
case CoyieldExprClass:
// These always have a side-effect.
return true;

View File

@ -186,6 +186,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) {
case Expr::CXXFoldExprClass:
case Expr::NoInitExprClass:
case Expr::DesignatedInitUpdateExprClass:
case Expr::CoyieldExprClass:
return Cl::CL_PRValue;
// Next come the complicated cases.
@ -397,6 +398,9 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) {
assert(cast<InitListExpr>(E)->getNumInits() == 1 &&
"Only 1-element init lists can be glvalues.");
return ClassifyInternal(Ctx, cast<InitListExpr>(E)->getInit(0));
case Expr::CoawaitExprClass:
return ClassifyInternal(Ctx, cast<CoawaitExpr>(E)->getResumeExpr());
}
llvm_unreachable("unhandled expression kind in classification");

View File

@ -9017,6 +9017,8 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) {
case Expr::AtomicExprClass:
case Expr::LambdaExprClass:
case Expr::CXXFoldExprClass:
case Expr::CoawaitExprClass:
case Expr::CoyieldExprClass:
return ICEDiag(IK_NotICE, E->getLocStart());
case Expr::InitListExprClass: {
@ -9102,6 +9104,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) {
case UO_PreDec:
case UO_AddrOf:
case UO_Deref:
case UO_Coawait:
// C99 6.6/3 allows increment and decrement within unevaluated
// subexpressions of constant expressions, but they can never be ICEs
// because an ICE cannot contain an lvalue operand.

View File

@ -3514,6 +3514,18 @@ recurse:
case Expr::CXXThisExprClass:
Out << "fpT";
break;
case Expr::CoawaitExprClass:
// FIXME: Propose a non-vendor mangling.
Out << "v18co_await";
mangleExpression(cast<CoawaitExpr>(E)->getOperand());
break;
case Expr::CoyieldExprClass:
// FIXME: Propose a non-vendor mangling.
Out << "v18co_yield";
mangleExpression(cast<CoawaitExpr>(E)->getOperand());
break;
}
}

View File

@ -52,8 +52,10 @@ CXXTryStmt::CXXTryStmt(SourceLocation tryLoc, Stmt *tryBlock,
CXXForRangeStmt::CXXForRangeStmt(DeclStmt *Range, DeclStmt *BeginEndStmt,
Expr *Cond, Expr *Inc, DeclStmt *LoopVar,
Stmt *Body, SourceLocation FL,
SourceLocation CL, SourceLocation RPL)
: Stmt(CXXForRangeStmtClass), ForLoc(FL), ColonLoc(CL), RParenLoc(RPL) {
SourceLocation CAL, SourceLocation CL,
SourceLocation RPL)
: Stmt(CXXForRangeStmtClass), ForLoc(FL), CoawaitLoc(CAL), ColonLoc(CL),
RParenLoc(RPL) {
SubExprs[RANGE] = Range;
SubExprs[BEGINEND] = BeginEndStmt;
SubExprs[COND] = Cond;

View File

@ -2163,6 +2163,31 @@ void StmtPrinter::VisitCXXFoldExpr(CXXFoldExpr *E) {
OS << ")";
}
// C++ Coroutines TS
void StmtPrinter::VisitCoroutineBodyStmt(CoroutineBodyStmt *S) {
Visit(S->getBody());
}
void StmtPrinter::VisitCoreturnStmt(CoreturnStmt *S) {
OS << "co_return";
if (S->getOperand()) {
OS << " ";
Visit(S->getOperand());
}
OS << ";";
}
void StmtPrinter::VisitCoawaitExpr(CoawaitExpr *S) {
OS << "co_await ";
PrintExpr(S->getOperand());
}
void StmtPrinter::VisitCoyieldExpr(CoyieldExpr *S) {
OS << "co_yield ";
PrintExpr(S->getOperand());
}
// Obj-C
void StmtPrinter::VisitObjCStringLiteral(ObjCStringLiteral *Node) {

View File

@ -1362,6 +1362,22 @@ void StmtProfiler::VisitCXXFoldExpr(const CXXFoldExpr *S) {
ID.AddInteger(S->getOperator());
}
void StmtProfiler::VisitCoroutineBodyStmt(const CoroutineBodyStmt *S) {
VisitStmt(S);
}
void StmtProfiler::VisitCoreturnStmt(const CoreturnStmt *S) {
VisitStmt(S);
}
void StmtProfiler::VisitCoawaitExpr(const CoawaitExpr *S) {
VisitExpr(S);
}
void StmtProfiler::VisitCoyieldExpr(const CoyieldExpr *S) {
VisitExpr(S);
}
void StmtProfiler::VisitOpaqueValueExpr(const OpaqueValueExpr *E) {
VisitExpr(E);
}

View File

@ -445,6 +445,7 @@ til::SExpr *SExprBuilder::translateUnaryOperator(const UnaryOperator *UO,
case UO_Real:
case UO_Imag:
case UO_Extension:
case UO_Coawait:
return new (Arena) til::Undefined(UO);
}
return new (Arena) til::Undefined(UO);

View File

@ -141,6 +141,10 @@ void CodeGenFunction::EmitStmt(const Stmt *S) {
case Stmt::SwitchStmtClass: EmitSwitchStmt(cast<SwitchStmt>(*S)); break;
case Stmt::GCCAsmStmtClass: // Intentional fall-through.
case Stmt::MSAsmStmtClass: EmitAsmStmt(cast<AsmStmt>(*S)); break;
case Stmt::CoroutineBodyStmtClass:
case Stmt::CoreturnStmtClass:
CGM.ErrorUnsupported(S, "coroutine");
break;
case Stmt::CapturedStmtClass: {
const CapturedStmt *CS = cast<CapturedStmt>(S);
EmitCapturedStmt(*CS, CS->getCapturedRegionKind());

View File

@ -1048,7 +1048,7 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
SourceLocation CoawaitLoc = ConsumeToken();
Res = ParseCastExpression(false);
if (!Res.isInvalid())
Res = Actions.ActOnCoawaitExpr(CoawaitLoc, Res.get());
Res = Actions.ActOnCoawaitExpr(getCurScope(), CoawaitLoc, Res.get());
return Res;
}

View File

@ -1568,7 +1568,7 @@ ExprResult Parser::ParseCoyieldExpression() {
SourceLocation Loc = ConsumeToken();
ExprResult Expr = ParseAssignmentExpression();
if (!Expr.isInvalid())
Expr = Actions.ActOnCoyieldExpr(Loc, Expr.get());
Expr = Actions.ActOnCoyieldExpr(getCurScope(), Loc, Expr.get());
return Expr;
}

View File

@ -1691,13 +1691,10 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) {
StmtResult ForEachStmt;
if (ForRange) {
ForRangeStmt = Actions.ActOnCXXForRangeStmt(ForLoc, CoawaitLoc,
FirstPart.get(),
ForRangeInit.ColonLoc,
ForRangeInit.RangeExpr.get(),
T.getCloseLocation(),
Sema::BFRK_Build);
ForRangeStmt = Actions.ActOnCXXForRangeStmt(
getCurScope(), ForLoc, CoawaitLoc, FirstPart.get(),
ForRangeInit.ColonLoc, ForRangeInit.RangeExpr.get(),
T.getCloseLocation(), Sema::BFRK_Build);
// Similarly, we need to do the semantic analysis for a for-range
// statement immediately in order to close over temporaries correctly.

View File

@ -33,11 +33,13 @@ void FunctionScopeInfo::Clear() {
ObjCWarnForNoDesignatedInitChain = false;
ObjCIsSecondaryInit = false;
ObjCWarnForNoInitDelegation = false;
FirstReturnLoc = SourceLocation();
FirstCXXTryLoc = SourceLocation();
FirstSEHTryLoc = SourceLocation();
SwitchStack.clear();
Returns.clear();
CoroutineStmts.clear();
ErrorTrap.reset();
PossiblyUnreachableDiags.clear();
WeakObjectUses.clear();

View File

@ -12,12 +12,89 @@
//===----------------------------------------------------------------------===//
#include "clang/Sema/SemaInternal.h"
#include "clang/AST/Decl.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/StmtCXX.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/Overload.h"
using namespace clang;
using namespace sema;
/// Look up the std::coroutine_traits<...>::promise_type for the given
/// function type.
static QualType lookupPromiseType(Sema &S, const FunctionProtoType *FnType,
SourceLocation Loc) {
// FIXME: Cache std::coroutine_traits once we've found it.
NamespaceDecl *Std = S.getStdNamespace();
if (!Std) {
S.Diag(Loc, diag::err_implied_std_coroutine_traits_not_found);
return QualType();
}
LookupResult Result(S, &S.PP.getIdentifierTable().get("coroutine_traits"),
Loc, Sema::LookupOrdinaryName);
if (!S.LookupQualifiedName(Result, Std)) {
S.Diag(Loc, diag::err_implied_std_coroutine_traits_not_found);
return QualType();
}
ClassTemplateDecl *CoroTraits = Result.getAsSingle<ClassTemplateDecl>();
if (!CoroTraits) {
Result.suppressDiagnostics();
// We found something weird. Complain about the first thing we found.
NamedDecl *Found = *Result.begin();
S.Diag(Found->getLocation(), diag::err_malformed_std_coroutine_traits);
return QualType();
}
// Form template argument list for coroutine_traits<R, P1, P2, ...>.
TemplateArgumentListInfo Args(Loc, Loc);
Args.addArgument(TemplateArgumentLoc(
TemplateArgument(FnType->getReturnType()),
S.Context.getTrivialTypeSourceInfo(FnType->getReturnType(), Loc)));
for (QualType T : FnType->getParamTypes())
Args.addArgument(TemplateArgumentLoc(
TemplateArgument(T), S.Context.getTrivialTypeSourceInfo(T, Loc)));
// Build the template-id.
QualType CoroTrait =
S.CheckTemplateIdType(TemplateName(CoroTraits), Loc, Args);
if (CoroTrait.isNull())
return QualType();
if (S.RequireCompleteType(Loc, CoroTrait,
diag::err_coroutine_traits_missing_specialization))
return QualType();
CXXRecordDecl *RD = CoroTrait->getAsCXXRecordDecl();
assert(RD && "specialization of class template is not a class?");
// Look up the ::promise_type member.
LookupResult R(S, &S.PP.getIdentifierTable().get("promise_type"), Loc,
Sema::LookupOrdinaryName);
S.LookupQualifiedName(R, RD);
auto *Promise = R.getAsSingle<TypeDecl>();
if (!Promise) {
S.Diag(Loc, diag::err_implied_std_coroutine_traits_promise_type_not_found)
<< RD;
return QualType();
}
// The promise type is required to be a class type.
QualType PromiseType = S.Context.getTypeDeclType(Promise);
if (!PromiseType->getAsCXXRecordDecl()) {
S.Diag(Loc, diag::err_implied_std_coroutine_traits_promise_type_not_class)
<< PromiseType;
return QualType();
}
return PromiseType;
}
/// Check that this is a context in which a coroutine suspension can appear.
static FunctionScopeInfo *
checkCoroutineContext(Sema &S, SourceLocation Loc, StringRef Keyword) {
// 'co_await' and 'co_yield' are permitted in unevaluated operands.
// FIXME: Not in 'noexcept'.
if (S.isUnevaluatedContext())
return nullptr;
@ -42,36 +119,143 @@ checkCoroutineContext(Sema &S, SourceLocation Loc, StringRef Keyword) {
} else {
auto *ScopeInfo = S.getCurFunction();
assert(ScopeInfo && "missing function scope for function");
// If we don't have a promise variable, build one now.
if (!ScopeInfo->CoroutinePromise && !FD->getType()->isDependentType()) {
QualType T =
lookupPromiseType(S, FD->getType()->castAs<FunctionProtoType>(), Loc);
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, false);
}
return ScopeInfo;
}
return nullptr;
}
ExprResult Sema::ActOnCoawaitExpr(SourceLocation Loc, Expr *E) {
auto *Context = checkCoroutineContext(*this, Loc, "co_await");
ExprResult Res = ExprError();
/// 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);
}
if (Context && !Res.isInvalid())
Context->CoroutineStmts.push_back(Res.get());
struct ReadySuspendResumeResult {
bool IsInvalid;
Expr *Results[3];
};
/// Build calls to await_ready, await_suspend, and await_resume for a co_await
/// expression.
static ReadySuspendResumeResult buildCoawaitCalls(Sema &S, SourceLocation Loc,
Expr *E) {
// Assume invalid until we see otherwise.
ReadySuspendResumeResult Calls = {true, {}};
const StringRef Funcs[] = {"await_ready", "await_suspend", "await_resume"};
for (size_t I = 0, N = llvm::array_lengthof(Funcs); I != N; ++I) {
DeclarationNameInfo NameInfo(&S.PP.getIdentifierTable().get(Funcs[I]), Loc);
Expr *Operand = new (S.Context) OpaqueValueExpr(
Loc, E->getType(), E->getValueKind(), E->getObjectKind(), E);
// FIXME: Fix BuildMemberReferenceExpr to take a const CXXScopeSpec&.
CXXScopeSpec SS;
ExprResult Result = S.BuildMemberReferenceExpr(
Operand, Operand->getType(), Loc, /*IsPtr=*/false, SS,
SourceLocation(), nullptr, NameInfo, /*TemplateArgs=*/nullptr,
/*Scope=*/nullptr);
if (Result.isInvalid())
return Calls;
// FIXME: Pass coroutine handle to await_suspend.
Result = S.ActOnCallExpr(nullptr, Result.get(), Loc, None, Loc, nullptr);
if (Result.isInvalid())
return Calls;
Calls.Results[I] = Result.get();
}
Calls.IsInvalid = false;
return Calls;
}
ExprResult Sema::ActOnCoawaitExpr(Scope *S, SourceLocation Loc, Expr *E) {
ExprResult Awaitable = buildOperatorCoawaitCall(*this, S, Loc, E);
if (Awaitable.isInvalid())
return ExprError();
return BuildCoawaitExpr(Loc, Awaitable.get());
}
ExprResult Sema::BuildCoawaitExpr(SourceLocation Loc, Expr *E) {
auto *Coroutine = checkCoroutineContext(*this, Loc, "co_await");
if (E->getType()->isDependentType()) {
Expr *Res = new (Context) CoawaitExpr(Loc, Context.DependentTy, E);
if (Coroutine)
Coroutine->CoroutineStmts.push_back(Res);
return Res;
}
if (E->getType()->isPlaceholderType()) {
ExprResult R = CheckPlaceholderExpr(E);
if (R.isInvalid()) return ExprError();
E = R.get();
}
// FIXME: If E is a prvalue, create a temporary.
// FIXME: If E is an xvalue, convert to lvalue.
// Build the await_ready, await_suspend, await_resume calls.
ReadySuspendResumeResult RSS = buildCoawaitCalls(*this, Loc, E);
if (RSS.IsInvalid)
return ExprError();
Expr *Res = new (Context) CoawaitExpr(Loc, E, RSS.Results[0], RSS.Results[1],
RSS.Results[2]);
if (Coroutine)
Coroutine->CoroutineStmts.push_back(Res);
return Res;
}
ExprResult Sema::ActOnCoyieldExpr(SourceLocation Loc, Expr *E) {
auto *Context = checkCoroutineContext(*this, Loc, "co_yield");
ExprResult Res = ExprError();
ExprResult Sema::ActOnCoyieldExpr(Scope *S, SourceLocation Loc, Expr *E) {
// FIXME: Build yield_value call.
ExprResult Awaitable = buildOperatorCoawaitCall(*this, S, Loc, E);
if (Awaitable.isInvalid())
return ExprError();
return BuildCoyieldExpr(Loc, Awaitable.get());
}
ExprResult Sema::BuildCoyieldExpr(SourceLocation Loc, Expr *E) {
auto *Coroutine = checkCoroutineContext(*this, Loc, "co_yield");
if (Context && !Res.isInvalid())
Context->CoroutineStmts.push_back(Res.get());
// FIXME: Build await_* calls.
Expr *Res = new (Context) CoyieldExpr(Loc, Context.VoidTy, E);
if (Coroutine)
Coroutine->CoroutineStmts.push_back(Res);
return Res;
}
StmtResult Sema::ActOnCoreturnStmt(SourceLocation Loc, Expr *E) {
auto *Context = checkCoroutineContext(*this, Loc, "co_return");
StmtResult Res = StmtError();
return BuildCoreturnStmt(Loc, E);
}
StmtResult Sema::BuildCoreturnStmt(SourceLocation Loc, Expr *E) {
auto *Coroutine = checkCoroutineContext(*this, Loc, "co_return");
if (Context && !Res.isInvalid())
Context->CoroutineStmts.push_back(Res.get());
// FIXME: Build return_* calls.
Stmt *Res = new (Context) CoreturnStmt(Loc, E);
if (Coroutine)
Coroutine->CoroutineStmts.push_back(Res);
return Res;
}
@ -81,26 +265,25 @@ void Sema::CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *Body) {
// Coroutines [stmt.return]p1:
// A return statement shall not appear in a coroutine.
if (!Fn->Returns.empty()) {
Diag(Fn->Returns.front()->getLocStart(), diag::err_return_in_coroutine);
if (Fn->FirstReturnLoc.isValid()) {
Diag(Fn->FirstReturnLoc, diag::err_return_in_coroutine);
auto *First = Fn->CoroutineStmts[0];
Diag(First->getLocStart(), diag::note_declared_coroutine_here)
<< 0; // FIXME: Indicate the kind here
<< (isa<CoawaitExpr>(First) ? 0 :
isa<CoyieldExpr>(First) ? 1 : 2);
}
bool AnyCoawaits = false;
bool AnyCoyields = false;
for (auto *CoroutineStmt : Fn->CoroutineStmts) {
(void)CoroutineStmt;
AnyCoawaits = AnyCoyields = true; // FIXME
AnyCoawaits |= isa<CoawaitExpr>(CoroutineStmt);
AnyCoyields |= isa<CoyieldExpr>(CoroutineStmt);
}
if (!AnyCoawaits && !AnyCoyields)
Diag(Fn->CoroutineStmts.front()->getLocStart(),
diag::ext_coroutine_without_coawait_coyield);
diag::ext_coroutine_without_co_await_co_yield);
// FIXME: If we have a deduced return type, resolve it now.
// FIXME: Compute the promise type.
// FIXME: Perform analysis of initial and final suspend, and set_exception call.
// FIXME: Complete the semantic analysis of the CoroutineStmts.
// FIXME: Perform analysis of initial and final suspend,
// and set_exception call.
}

View File

@ -1063,8 +1063,10 @@ CanThrowResult Sema::canThrow(const Expr *E) {
// Many other things have subexpressions, so we have to test those.
// Some are simple:
case Expr::CoawaitExprClass:
case Expr::ConditionalOperatorClass:
case Expr::CompoundLiteralExprClass:
case Expr::CoyieldExprClass:
case Expr::CXXConstCastExprClass:
case Expr::CXXReinterpretCastExprClass:
case Expr::CXXStdInitializerListExprClass:

View File

@ -10901,6 +10901,7 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc,
}
break;
case UO_Extension:
case UO_Coawait:
resultType = Input.get()->getType();
VK = Input.get()->getValueKind();
OK = Input.get()->getObjectKind();

View File

@ -8228,15 +8228,16 @@ void Sema::AddBuiltinOperatorCandidates(OverloadedOperatorKind Op,
case OO_Array_New:
case OO_Array_Delete:
case OO_Call:
case OO_Coawait:
llvm_unreachable(
"Special operators don't use AddBuiltinOperatorCandidates");
case OO_Comma:
case OO_Arrow:
case OO_Coawait:
// C++ [over.match.oper]p3:
// -- For the operator ',', the unary operator '&', or the
// operator '->', the built-in candidates set is empty.
// -- For the operator ',', the unary operator '&', the
// operator '->', or the operator 'co_await', the
// built-in candidates set is empty.
break;
case OO_Plus: // '+' is either unary or binary
@ -12481,13 +12482,14 @@ ExprResult Sema::BuildLiteralOperatorCall(LookupResult &R,
/// otherwise CallExpr is set to ExprError() and some non-success value
/// is returned.
Sema::ForRangeStatus
Sema::BuildForRangeBeginEndCall(Scope *S, SourceLocation Loc,
SourceLocation RangeLoc, VarDecl *Decl,
BeginEndFunction BEF,
Sema::BuildForRangeBeginEndCall(SourceLocation Loc,
SourceLocation RangeLoc,
const DeclarationNameInfo &NameInfo,
LookupResult &MemberLookup,
OverloadCandidateSet *CandidateSet,
Expr *Range, ExprResult *CallExpr) {
Scope *S = nullptr;
CandidateSet->clear();
if (!MemberLookup.empty()) {
ExprResult MemberRef =
@ -12499,15 +12501,11 @@ Sema::BuildForRangeBeginEndCall(Scope *S, SourceLocation Loc,
/*TemplateArgs=*/nullptr, S);
if (MemberRef.isInvalid()) {
*CallExpr = ExprError();
Diag(Range->getLocStart(), diag::note_in_for_range)
<< RangeLoc << BEF << Range->getType();
return FRS_DiagnosticIssued;
}
*CallExpr = ActOnCallExpr(S, MemberRef.get(), Loc, None, Loc, nullptr);
if (CallExpr->isInvalid()) {
*CallExpr = ExprError();
Diag(Range->getLocStart(), diag::note_in_for_range)
<< RangeLoc << BEF << Range->getType();
return FRS_DiagnosticIssued;
}
} else {
@ -12538,8 +12536,6 @@ Sema::BuildForRangeBeginEndCall(Scope *S, SourceLocation Loc,
/*AllowTypoCorrection=*/false);
if (CallExpr->isInvalid() || OverloadResult != OR_Success) {
*CallExpr = ExprError();
Diag(Range->getLocStart(), diag::note_in_for_range)
<< RangeLoc << BEF << Range->getType();
return FRS_DiagnosticIssued;
}
}

View File

@ -1859,13 +1859,19 @@ static bool FinishForRangeVarDecl(Sema &SemaRef, VarDecl *Decl, Expr *Init,
}
namespace {
// An enum to represent whether something is dealing with a call to begin()
// or a call to end() in a range-based for loop.
enum BeginEndFunction {
BEF_begin,
BEF_end
};
/// Produce a note indicating which begin/end function was implicitly called
/// by a C++11 for-range statement. This is often not obvious from the code,
/// nor from the diagnostics produced when analysing the implicit expressions
/// required in a for-range statement.
void NoteForRangeBeginEndFunction(Sema &SemaRef, Expr *E,
Sema::BeginEndFunction BEF) {
BeginEndFunction BEF) {
CallExpr *CE = dyn_cast<CallExpr>(E);
if (!CE)
return;
@ -1923,10 +1929,11 @@ static bool ObjCEnumerationCollection(Expr *Collection) {
///
/// The body of the loop is not available yet, since it cannot be analysed until
/// we have determined the type of the for-range-declaration.
StmtResult
Sema::ActOnCXXForRangeStmt(SourceLocation ForLoc, SourceLocation CoawaitLoc,
Stmt *First, SourceLocation ColonLoc, Expr *Range,
SourceLocation RParenLoc, BuildForRangeKind Kind) {
StmtResult Sema::ActOnCXXForRangeStmt(Scope *S, SourceLocation ForLoc,
SourceLocation CoawaitLoc, Stmt *First,
SourceLocation ColonLoc, Expr *Range,
SourceLocation RParenLoc,
BuildForRangeKind Kind) {
if (!First)
return StmtError();
@ -1950,7 +1957,7 @@ Sema::ActOnCXXForRangeStmt(SourceLocation ForLoc, SourceLocation CoawaitLoc,
// Coroutines: 'for co_await' implicitly co_awaits its range.
if (CoawaitLoc.isValid()) {
ExprResult Coawait = ActOnCoawaitExpr(CoawaitLoc, Range);
ExprResult Coawait = ActOnCoawaitExpr(S, CoawaitLoc, Range);
if (Coawait.isInvalid()) return StmtError();
Range = Coawait.get();
}
@ -1990,7 +1997,7 @@ Sema::ActOnCXXForRangeStmt(SourceLocation ForLoc, SourceLocation CoawaitLoc,
/// BeginExpr and EndExpr are set and FRS_Success is returned on success;
/// CandidateSet and BEF are set and some non-success value is returned on
/// failure.
static Sema::ForRangeStatus BuildNonArrayForRange(Sema &SemaRef, Scope *S,
static Sema::ForRangeStatus BuildNonArrayForRange(Sema &SemaRef,
Expr *BeginRange, Expr *EndRange,
QualType RangeType,
VarDecl *BeginVar,
@ -1999,7 +2006,7 @@ static Sema::ForRangeStatus BuildNonArrayForRange(Sema &SemaRef, Scope *S,
OverloadCandidateSet *CandidateSet,
ExprResult *BeginExpr,
ExprResult *EndExpr,
Sema::BeginEndFunction *BEF) {
BeginEndFunction *BEF) {
DeclarationNameInfo BeginNameInfo(
&SemaRef.PP.getIdentifierTable().get("begin"), ColonLoc);
DeclarationNameInfo EndNameInfo(&SemaRef.PP.getIdentifierTable().get("end"),
@ -2020,7 +2027,7 @@ static Sema::ForRangeStatus BuildNonArrayForRange(Sema &SemaRef, Scope *S,
if (BeginMemberLookup.empty() != EndMemberLookup.empty()) {
SourceLocation RangeLoc = BeginVar->getLocation();
*BEF = BeginMemberLookup.empty() ? Sema::BEF_end : Sema::BEF_begin;
*BEF = BeginMemberLookup.empty() ? BEF_end : BEF_begin;
SemaRef.Diag(RangeLoc, diag::err_for_range_member_begin_end_mismatch)
<< RangeLoc << BeginRange->getType() << *BEF;
@ -2034,29 +2041,35 @@ static Sema::ForRangeStatus BuildNonArrayForRange(Sema &SemaRef, Scope *S,
}
*BEF = Sema::BEF_begin;
*BEF = BEF_begin;
Sema::ForRangeStatus RangeStatus =
SemaRef.BuildForRangeBeginEndCall(S, ColonLoc, ColonLoc, BeginVar,
Sema::BEF_begin, BeginNameInfo,
SemaRef.BuildForRangeBeginEndCall(ColonLoc, ColonLoc, BeginNameInfo,
BeginMemberLookup, CandidateSet,
BeginRange, BeginExpr);
if (RangeStatus != Sema::FRS_Success)
if (RangeStatus != Sema::FRS_Success) {
if (RangeStatus == Sema::FRS_DiagnosticIssued)
SemaRef.Diag(BeginRange->getLocStart(), diag::note_in_for_range)
<< ColonLoc << BEF_begin << BeginRange->getType();
return RangeStatus;
}
if (FinishForRangeVarDecl(SemaRef, BeginVar, BeginExpr->get(), ColonLoc,
diag::err_for_range_iter_deduction_failure)) {
NoteForRangeBeginEndFunction(SemaRef, BeginExpr->get(), *BEF);
return Sema::FRS_DiagnosticIssued;
}
*BEF = Sema::BEF_end;
*BEF = BEF_end;
RangeStatus =
SemaRef.BuildForRangeBeginEndCall(S, ColonLoc, ColonLoc, EndVar,
Sema::BEF_end, EndNameInfo,
SemaRef.BuildForRangeBeginEndCall(ColonLoc, ColonLoc, EndNameInfo,
EndMemberLookup, CandidateSet,
EndRange, EndExpr);
if (RangeStatus != Sema::FRS_Success)
if (RangeStatus != Sema::FRS_Success) {
if (RangeStatus == Sema::FRS_DiagnosticIssued)
SemaRef.Diag(EndRange->getLocStart(), diag::note_in_for_range)
<< ColonLoc << BEF_end << EndRange->getType();
return RangeStatus;
}
if (FinishForRangeVarDecl(SemaRef, EndVar, EndExpr->get(), ColonLoc,
diag::err_for_range_iter_deduction_failure)) {
NoteForRangeBeginEndFunction(SemaRef, EndExpr->get(), *BEF);
@ -2086,10 +2099,9 @@ static StmtResult RebuildForRangeWithDereference(Sema &SemaRef, Scope *S,
if (AdjustedRange.isInvalid())
return StmtResult();
StmtResult SR =
SemaRef.ActOnCXXForRangeStmt(ForLoc, CoawaitLoc, LoopVarDecl, ColonLoc,
AdjustedRange.get(), RParenLoc,
Sema::BFRK_Check);
StmtResult SR = SemaRef.ActOnCXXForRangeStmt(
S, ForLoc, CoawaitLoc, LoopVarDecl, ColonLoc, AdjustedRange.get(),
RParenLoc, Sema::BFRK_Check);
if (SR.isInvalid())
return StmtResult();
}
@ -2099,8 +2111,8 @@ static StmtResult RebuildForRangeWithDereference(Sema &SemaRef, Scope *S,
// case there are any other (non-fatal) problems with it.
SemaRef.Diag(RangeLoc, diag::err_for_range_dereference)
<< Range->getType() << FixItHint::CreateInsertion(RangeLoc, "*");
return SemaRef.ActOnCXXForRangeStmt(ForLoc, CoawaitLoc, LoopVarDecl, ColonLoc,
AdjustedRange.get(), RParenLoc,
return SemaRef.ActOnCXXForRangeStmt(S, ForLoc, CoawaitLoc, LoopVarDecl,
ColonLoc, AdjustedRange.get(), RParenLoc,
Sema::BFRK_Rebuild);
}
@ -2127,6 +2139,15 @@ Sema::BuildCXXForRangeStmt(SourceLocation ForLoc, SourceLocation CoawaitLoc,
Stmt *RangeDecl, Stmt *BeginEnd, Expr *Cond,
Expr *Inc, Stmt *LoopVarDecl,
SourceLocation RParenLoc, BuildForRangeKind Kind) {
// FIXME: This should not be used during template instantiation. We should
// pick up the set of unqualified lookup results for the != and + operators
// in the initial parse.
//
// Testcase (accepts-invalid):
// template<typename T> void f() { for (auto x : T()) {} }
// namespace N { struct X { X begin(); X end(); int operator*(); }; }
// bool operator!=(N::X, N::X); void operator++(N::X);
// void g() { f<N::X>(); }
Scope *S = getCurScope();
DeclStmt *RangeDS = cast<DeclStmt>(RangeDecl);
@ -2226,9 +2247,9 @@ Sema::BuildCXXForRangeStmt(SourceLocation ForLoc, SourceLocation CoawaitLoc,
} else {
OverloadCandidateSet CandidateSet(RangeLoc,
OverloadCandidateSet::CSK_Normal);
Sema::BeginEndFunction BEFFailure;
BeginEndFunction BEFFailure;
ForRangeStatus RangeStatus =
BuildNonArrayForRange(*this, S, BeginRangeRef.get(),
BuildNonArrayForRange(*this, BeginRangeRef.get(),
EndRangeRef.get(), RangeType,
BeginVar, EndVar, ColonLoc, &CandidateSet,
&BeginExpr, &EndExpr, &BEFFailure);
@ -2325,7 +2346,7 @@ Sema::BuildCXXForRangeStmt(SourceLocation ForLoc, SourceLocation CoawaitLoc,
IncrExpr = ActOnUnaryOp(S, ColonLoc, tok::plusplus, BeginRef.get());
if (!IncrExpr.isInvalid() && CoawaitLoc.isValid())
IncrExpr = ActOnCoawaitExpr(CoawaitLoc, IncrExpr.get());
IncrExpr = ActOnCoawaitExpr(S, CoawaitLoc, IncrExpr.get());
if (!IncrExpr.isInvalid())
IncrExpr = ActOnFinishFullExpr(IncrExpr.get());
if (IncrExpr.isInvalid()) {
@ -2364,10 +2385,10 @@ Sema::BuildCXXForRangeStmt(SourceLocation ForLoc, SourceLocation CoawaitLoc,
if (Kind == BFRK_Check)
return StmtResult();
// FIXME: Pass in CoawaitLoc in the dependent case.
return new (Context) CXXForRangeStmt(
RangeDS, cast_or_null<DeclStmt>(BeginEndDecl.get()), NotEqExpr.get(),
IncrExpr.get(), LoopVarDS, /*Body=*/nullptr, ForLoc, ColonLoc, RParenLoc);
IncrExpr.get(), LoopVarDS, /*Body=*/nullptr, ForLoc, CoawaitLoc,
ColonLoc, RParenLoc);
}
/// FinishObjCForCollectionStmt - Attach the body to a objective-C foreach
@ -2925,6 +2946,9 @@ Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
if (CurCap->HasImplicitReturnType || NRVOCandidate)
FunctionScopes.back()->Returns.push_back(Result);
if (FunctionScopes.back()->FirstReturnLoc.isInvalid())
FunctionScopes.back()->FirstReturnLoc = ReturnLoc;
return Result;
}
@ -3291,6 +3315,9 @@ StmtResult Sema::BuildReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
if (Result->getNRVOCandidate())
FunctionScopes.back()->Returns.push_back(Result);
if (FunctionScopes.back()->FirstReturnLoc.isInvalid())
FunctionScopes.back()->FirstReturnLoc = ReturnLoc;
return Result;
}

View File

@ -1287,6 +1287,30 @@ public:
Constraints, Clobbers, Exprs, EndLoc);
}
/// \brief Build a new co_return statement.
///
/// 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);
}
/// \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);
}
/// \brief Build a new co_yield expression.
///
/// By default, performs semantic analysis to build the new expression.
/// Subclasses may override this routine to provide different behavior.
ExprResult RebuildCoyieldExpr(SourceLocation CoyieldLoc, Expr *Result) {
return getSema().BuildCoyieldExpr(CoyieldLoc, Result);
}
/// \brief Build a new Objective-C \@try statement.
///
/// By default, performs semantic analysis to build the new statement.
@ -1715,6 +1739,7 @@ public:
/// By default, performs semantic analysis to build the new statement.
/// Subclasses may override this routine to provide different behavior.
StmtResult RebuildCXXForRangeStmt(SourceLocation ForLoc,
SourceLocation CoawaitLoc,
SourceLocation ColonLoc,
Stmt *Range, Stmt *BeginEnd,
Expr *Cond, Expr *Inc,
@ -1737,7 +1762,6 @@ public:
}
}
SourceLocation CoawaitLoc; // FIXME
return getSema().BuildCXXForRangeStmt(ForLoc, CoawaitLoc, ColonLoc,
Range, BeginEnd,
Cond, Inc, LoopVar, RParenLoc,
@ -6403,6 +6427,56 @@ TreeTransform<Derived>::TransformMSAsmStmt(MSAsmStmt *S) {
TransformedExprs, S->getEndLoc());
}
// C++ Coroutines TS
template<typename Derived>
StmtResult
TreeTransform<Derived>::TransformCoroutineBodyStmt(CoroutineBodyStmt *S) {
// The coroutine body should be re-formed by the caller if necessary.
return getDerived().TransformStmt(S->getBody());
}
template<typename Derived>
StmtResult
TreeTransform<Derived>::TransformCoreturnStmt(CoreturnStmt *S) {
ExprResult Result = getDerived().TransformInitializer(S->getOperand(),
/*NotCopyInit*/false);
if (Result.isInvalid())
return StmtError();
// 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());
}
template<typename Derived>
ExprResult
TreeTransform<Derived>::TransformCoawaitExpr(CoawaitExpr *E) {
ExprResult Result = getDerived().TransformInitializer(E->getOperand(),
/*NotCopyInit*/false);
if (Result.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().RebuildCoawaitExpr(E->getKeywordLoc(), Result.get());
}
template<typename Derived>
ExprResult
TreeTransform<Derived>::TransformCoyieldExpr(CoyieldExpr *E) {
ExprResult Result = getDerived().TransformInitializer(E->getOperand(),
/*NotCopyInit*/false);
if (Result.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().RebuildCoyieldExpr(E->getKeywordLoc(), Result.get());
}
// Objective-C Statements.
template<typename Derived>
StmtResult
TreeTransform<Derived>::TransformObjCAtTryStmt(ObjCAtTryStmt *S) {
@ -6692,6 +6766,7 @@ TreeTransform<Derived>::TransformCXXForRangeStmt(CXXForRangeStmt *S) {
Inc.get() != S->getInc() ||
LoopVar.get() != S->getLoopVarStmt()) {
NewStmt = getDerived().RebuildCXXForRangeStmt(S->getForLoc(),
S->getCoawaitLoc(),
S->getColonLoc(), Range.get(),
BeginEnd.get(), Cond.get(),
Inc.get(), LoopVar.get(),
@ -6708,6 +6783,7 @@ TreeTransform<Derived>::TransformCXXForRangeStmt(CXXForRangeStmt *S) {
// it now so we have a new statement to attach the body to.
if (Body.get() != S->getBody() && NewStmt.get() == S) {
NewStmt = getDerived().RebuildCXXForRangeStmt(S->getForLoc(),
S->getCoawaitLoc(),
S->getColonLoc(), Range.get(),
BeginEnd.get(), Cond.get(),
Inc.get(), LoopVar.get(),

View File

@ -381,6 +381,26 @@ void ASTStmtReader::VisitMSAsmStmt(MSAsmStmt *S) {
Constraints, Exprs, Clobbers);
}
void ASTStmtReader::VisitCoroutineBodyStmt(CoroutineBodyStmt *S) {
// FIXME: Implement coroutine serialization.
llvm_unreachable("unimplemented");
}
void ASTStmtReader::VisitCoreturnStmt(CoreturnStmt *S) {
// FIXME: Implement coroutine serialization.
llvm_unreachable("unimplemented");
}
void ASTStmtReader::VisitCoawaitExpr(CoawaitExpr *S) {
// FIXME: Implement coroutine serialization.
llvm_unreachable("unimplemented");
}
void ASTStmtReader::VisitCoyieldExpr(CoyieldExpr *S) {
// FIXME: Implement coroutine serialization.
llvm_unreachable("unimplemented");
}
void ASTStmtReader::VisitCapturedStmt(CapturedStmt *S) {
VisitStmt(S);
++Idx;
@ -1178,9 +1198,10 @@ void ASTStmtReader::VisitCXXTryStmt(CXXTryStmt *S) {
void ASTStmtReader::VisitCXXForRangeStmt(CXXForRangeStmt *S) {
VisitStmt(S);
S->setForLoc(ReadSourceLocation(Record, Idx));
S->setColonLoc(ReadSourceLocation(Record, Idx));
S->setRParenLoc(ReadSourceLocation(Record, Idx));
S->ForLoc = ReadSourceLocation(Record, Idx);
S->CoawaitLoc = ReadSourceLocation(Record, Idx);
S->ColonLoc = ReadSourceLocation(Record, Idx);
S->RParenLoc = ReadSourceLocation(Record, Idx);
S->setRangeStmt(Reader.ReadSubStmt());
S->setBeginEndStmt(Reader.ReadSubStmt());
S->setCond(Reader.ReadSubExpr());

View File

@ -287,6 +287,26 @@ void ASTStmtWriter::VisitMSAsmStmt(MSAsmStmt *S) {
Code = serialization::STMT_MSASM;
}
void ASTStmtWriter::VisitCoroutineBodyStmt(CoroutineBodyStmt *S) {
// FIXME: Implement coroutine serialization.
llvm_unreachable("unimplemented");
}
void ASTStmtWriter::VisitCoreturnStmt(CoreturnStmt *S) {
// FIXME: Implement coroutine serialization.
llvm_unreachable("unimplemented");
}
void ASTStmtWriter::VisitCoawaitExpr(CoawaitExpr *S) {
// FIXME: Implement coroutine serialization.
llvm_unreachable("unimplemented");
}
void ASTStmtWriter::VisitCoyieldExpr(CoyieldExpr *S) {
// FIXME: Implement coroutine serialization.
llvm_unreachable("unimplemented");
}
void ASTStmtWriter::VisitCapturedStmt(CapturedStmt *S) {
VisitStmt(S);
// NumCaptures
@ -1135,6 +1155,7 @@ void ASTStmtWriter::VisitCXXTryStmt(CXXTryStmt *S) {
void ASTStmtWriter::VisitCXXForRangeStmt(CXXForRangeStmt *S) {
VisitStmt(S);
Writer.AddSourceLocation(S->getForLoc(), Record);
Writer.AddSourceLocation(S->getCoawaitLoc(), Record);
Writer.AddSourceLocation(S->getColonLoc(), Record);
Writer.AddSourceLocation(S->getRParenLoc(), Record);
Writer.AddStmt(S->getRangeStmt());

View File

@ -765,6 +765,10 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::PackExpansionExprClass:
case Stmt::SubstNonTypeTemplateParmPackExprClass:
case Stmt::FunctionParmPackExprClass:
case Stmt::CoroutineBodyStmtClass:
case Stmt::CoawaitExprClass:
case Stmt::CoreturnStmtClass:
case Stmt::CoyieldExprClass:
case Stmt::SEHTryStmtClass:
case Stmt::SEHExceptStmtClass:
case Stmt::SEHLeaveStmtClass:

View File

@ -9,7 +9,7 @@ U f(T t) {
1 + co_yield t; // expected-error {{expected expression}}
auto x = co_await t;
auto y = co_yield t;
auto y = co_yield t; // expected-error {{void}} FIXME
for co_await (int x : t) {}
for co_await (int x = 0; x != 10; ++x) {} // expected-error {{'co_await' modifier can only be applied to range-based for loop}}

View File

@ -1,37 +1,68 @@
// RUN: %clang_cc1 -std=c++14 -fcoroutines -verify %s
struct awaitable {
bool await_ready();
void await_suspend(); // FIXME: coroutine_handle
void await_resume();
} a;
void no_coroutine_traits() {
co_await a; // expected-error {{need to include <coroutine>}}
}
namespace std {
template<typename ...T> struct coroutine_traits; // expected-note {{declared here}}
};
void no_specialization() {
co_await a; // expected-error {{implicit instantiation of undefined template 'std::coroutine_traits<void>'}}
}
template<typename ...T> struct std::coroutine_traits<int, T...> {};
int no_promise_type() {
co_await a; // expected-error {{this function cannot be a coroutine: 'coroutine_traits<int>' has no member named 'promise_type'}}
}
struct promise; // expected-note {{forward declaration}}
template<typename ...T> struct std::coroutine_traits<void, T...> { using promise_type = promise; };
// FIXME: This diagnostic is terrible.
void undefined_promise() { // expected-error {{variable has incomplete type 'promise_type'}}
co_await a;
}
struct promise {};
void mixed_yield() {
// FIXME: diagnose
co_yield 0;
return;
co_yield 0; // expected-note {{use of 'co_yield'}}
return; // expected-error {{not allowed in coroutine}}
}
void mixed_await() {
// FIXME: diagnose
co_await 0;
return;
co_await a; // expected-note {{use of 'co_await'}}
return; // expected-error {{not allowed in coroutine}}
}
void only_coreturn() {
// FIXME: diagnose
co_return;
co_return; // expected-warning {{'co_return' used in a function that uses neither 'co_await' nor 'co_yield'}}
}
void mixed_coreturn(bool b) {
// FIXME: diagnose
if (b)
co_return;
// expected-warning@+1 {{'co_return' used in a function that uses neither}}
co_return; // expected-note {{use of 'co_return'}}
else
return;
return; // expected-error {{not allowed in coroutine}}
}
struct CtorDtor {
CtorDtor() {
co_yield 0; // expected-error {{'co_yield' cannot be used in a constructor}}
}
CtorDtor(int n) {
CtorDtor(awaitable a) {
// The spec doesn't say this is ill-formed, but it must be.
co_await n; // expected-error {{'co_await' cannot be used in a constructor}}
co_await a; // expected-error {{'co_await' cannot be used in a constructor}}
}
~CtorDtor() {
co_return 0; // expected-error {{'co_return' cannot be used in a destructor}}
@ -42,10 +73,35 @@ struct CtorDtor {
}
};
constexpr void constexpr_coroutine() {
co_yield 0; // expected-error {{'co_yield' cannot be used in a constexpr function}}
constexpr void constexpr_coroutine() { // expected-error {{never produces a constant expression}}
co_yield 0; // expected-error {{'co_yield' cannot be used in a constexpr function}} expected-note {{subexpression}}
}
void varargs_coroutine(const char *, ...) {
co_await 0; // expected-error {{'co_await' cannot be used in a varargs function}}
co_await a; // expected-error {{'co_await' cannot be used in a varargs function}}
}
struct outer {};
namespace dependent_operator_co_await_lookup {
template<typename T> void await_template(T t) {
// no unqualified lookup results
co_await t; // expected-error {{no member named 'await_ready' in 'dependent_operator_co_await_lookup::not_awaitable'}}
// expected-error@-1 {{call to function 'operator co_await' that is neither visible in the template definition nor found by argument-dependent lookup}}
};
template void await_template(awaitable);
struct indirectly_awaitable { indirectly_awaitable(outer); };
awaitable operator co_await(indirectly_awaitable); // expected-note {{should be declared prior to}}
template void await_template(indirectly_awaitable);
struct not_awaitable {};
template void await_template(not_awaitable); // expected-note {{instantiation}}
template<typename T> void await_template_2(T t) {
// one unqualified lookup result
co_await t;
};
template void await_template(outer); // expected-note {{instantiation}}
template void await_template_2(outer);
}

View File

@ -227,6 +227,10 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent,
case Stmt::AtomicExprClass:
case Stmt::BinaryConditionalOperatorClass:
case Stmt::TypeTraitExprClass:
case Stmt::CoroutineBodyStmtClass:
case Stmt::CoawaitExprClass:
case Stmt::CoreturnStmtClass:
case Stmt::CoyieldExprClass:
case Stmt::CXXBindTemporaryExprClass:
case Stmt::CXXDefaultArgExprClass:
case Stmt::CXXDefaultInitExprClass: