Skip to content

Commit

Permalink
[coroutines] Creation of promise object, lookup of operator co_await,…
Browse files Browse the repository at this point in the history
… building

of await_* calls, and AST representation for same.

llvm-svn: 251387
  • Loading branch information
zygoloid committed Oct 27, 2015
1 parent d5510d1 commit 9f690bd
Show file tree
Hide file tree
Showing 36 changed files with 871 additions and 132 deletions.
8 changes: 7 additions & 1 deletion clang/include/clang/AST/DataRecursiveASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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() \
Expand Down Expand Up @@ -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, {})
Expand Down
123 changes: 123 additions & 0 deletions clang/include/clang/AST/ExprCXX.h
Original file line number Diff line number Diff line change
Expand Up @@ -4008,6 +4008,129 @@ class CXXFoldExpr : public Expr {
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
3 changes: 2 additions & 1 deletion clang/include/clang/AST/OperationKinds.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
30 changes: 29 additions & 1 deletion clang/include/clang/AST/RecursiveASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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() \
Expand Down Expand Up @@ -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, {})
Expand Down
100 changes: 95 additions & 5 deletions clang/include/clang/AST/StmtCXX.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) { }


Expand Down Expand Up @@ -181,13 +185,10 @@ class CXXForRangeStmt : public Stmt {
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 {
Expand Down Expand Up @@ -287,6 +288,95 @@ class MSDependentExistsStmt : public Stmt {
}
};

/// \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
3 changes: 2 additions & 1 deletion clang/include/clang/AST/StmtVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ class StmtVisitorBase {
case UO_Real: DISPATCH(UnaryReal, UnaryOperator);
case UO_Imag: DISPATCH(UnaryImag, UnaryOperator);
case UO_Extension: DISPATCH(UnaryExtension, UnaryOperator);
case UO_Coawait: DISPATCH(UnaryCoawait, UnaryOperator);
}
}

Expand Down Expand Up @@ -158,7 +159,7 @@ class StmtVisitorBase {
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. :)
Expand Down
19 changes: 17 additions & 2 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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">;
Expand All @@ -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 {
Expand Down
Loading

0 comments on commit 9f690bd

Please sign in to comment.