r307513 - [coroutines] Include the implicit object parameter type when looking up coroutine_traits for member functions.
Eric Fiselier via cfe-commits
cfe-commits at lists.llvm.org
Sun Jul 9 18:27:22 PDT 2017
Author: ericwf
Date: Sun Jul 9 18:27:22 2017
New Revision: 307513
URL: http://llvm.org/viewvc/llvm-project?rev=307513&view=rev
Log:
[coroutines] Include the implicit object parameter type when looking up coroutine_traits for member functions.
This patch was originally from Toby Allsopp, but I hijacked it and
fixed it up with his permission.
Modified:
cfe/trunk/lib/Sema/SemaCoroutine.cpp
cfe/trunk/test/SemaCXX/coroutines.cpp
Modified: cfe/trunk/lib/Sema/SemaCoroutine.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaCoroutine.cpp?rev=307513&r1=307512&r2=307513&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaCoroutine.cpp (original)
+++ cfe/trunk/lib/Sema/SemaCoroutine.cpp Sun Jul 9 18:27:22 2017
@@ -43,9 +43,10 @@ static bool lookupMember(Sema &S, const
/// Look up the std::coroutine_traits<...>::promise_type for the given
/// function type.
-static QualType lookupPromiseType(Sema &S, const FunctionProtoType *FnType,
- SourceLocation KwLoc,
- SourceLocation FuncLoc) {
+static QualType lookupPromiseType(Sema &S, const FunctionDecl *FD,
+ SourceLocation KwLoc) {
+ const FunctionProtoType *FnType = FD->getType()->castAs<FunctionProtoType>();
+ const SourceLocation FuncLoc = FD->getLocation();
// FIXME: Cache std::coroutine_traits once we've found it.
NamespaceDecl *StdExp = S.lookupStdExperimentalNamespace();
if (!StdExp) {
@@ -71,16 +72,35 @@ static QualType lookupPromiseType(Sema &
return QualType();
}
- // Form template argument list for coroutine_traits<R, P1, P2, ...>.
+ // Form template argument list for coroutine_traits<R, P1, P2, ...> according
+ // to [dcl.fct.def.coroutine]3
TemplateArgumentListInfo Args(KwLoc, KwLoc);
- Args.addArgument(TemplateArgumentLoc(
- TemplateArgument(FnType->getReturnType()),
- S.Context.getTrivialTypeSourceInfo(FnType->getReturnType(), KwLoc)));
- // FIXME: If the function is a non-static member function, add the type
- // of the implicit object parameter before the formal parameters.
- for (QualType T : FnType->getParamTypes())
+ auto AddArg = [&](QualType T) {
Args.addArgument(TemplateArgumentLoc(
TemplateArgument(T), S.Context.getTrivialTypeSourceInfo(T, KwLoc)));
+ };
+ AddArg(FnType->getReturnType());
+ // If the function is a non-static member function, add the type
+ // of the implicit object parameter before the formal parameters.
+ if (auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
+ if (MD->isInstance()) {
+ // [over.match.funcs]4
+ // For non-static member functions, the type of the implicit object
+ // parameter is
+ // â âlvalue reference to cv Xâ for functions declared without a
+ // ref-qualifier or with the & ref-qualifier
+ // â ârvalue reference to cv Xâ for functions declared with the &&
+ // ref-qualifier
+ QualType T =
+ MD->getThisType(S.Context)->getAs<PointerType>()->getPointeeType();
+ T = FnType->getRefQualifier() == RQ_RValue
+ ? S.Context.getRValueReferenceType(T)
+ : S.Context.getLValueReferenceType(T, /*SpelledAsLValue*/ true);
+ AddArg(T);
+ }
+ }
+ for (QualType T : FnType->getParamTypes())
+ AddArg(T);
// Build the template-id.
QualType CoroTrait =
@@ -424,12 +444,16 @@ static ExprResult buildPromiseCall(Sema
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());
+ bool IsThisDependentType = [&] {
+ if (auto *MD = dyn_cast_or_null<CXXMethodDecl>(FD))
+ return MD->isInstance() && MD->getThisType(Context)->isDependentType();
+ else
+ return false;
+ }();
+
+ QualType T = FD->getType()->isDependentType() || IsThisDependentType
+ ? Context.DependentTy
+ : lookupPromiseType(*this, FD, Loc);
if (T.isNull())
return nullptr;
Modified: cfe/trunk/test/SemaCXX/coroutines.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/coroutines.cpp?rev=307513&r1=307512&r2=307513&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/coroutines.cpp (original)
+++ cfe/trunk/test/SemaCXX/coroutines.cpp Sun Jul 9 18:27:22 2017
@@ -22,8 +22,24 @@ void no_coroutine_traits() {
namespace std {
namespace experimental {
-template <typename... T>
-struct coroutine_traits; // expected-note {{declared here}}
+
+template <class... Args>
+struct void_t_imp {
+ using type = void;
+};
+template <class... Args>
+using void_t = typename void_t_imp<Args...>::type;
+
+template <class T, class = void>
+struct traits_sfinae_base {};
+
+template <class T>
+struct traits_sfinae_base<T, void_t<typename T::promise_type>> {
+ using promise_type = typename T::promise_type;
+};
+
+template <class Ret, class... Args>
+struct coroutine_traits : public traits_sfinae_base<Ret> {};
}} // namespace std::experimental
template<typename Promise> struct coro {};
@@ -50,8 +66,9 @@ struct suspend_never {
void await_resume() {}
};
-void no_specialization() {
- co_await a; // expected-error {{implicit instantiation of undefined template 'std::experimental::coroutine_traits<void>'}}
+struct DummyVoidTag {};
+DummyVoidTag no_specialization() { // expected-error {{this function cannot be a coroutine: 'std::experimental::coroutine_traits<DummyVoidTag>' has no member named 'promise_type'}}
+ co_await a;
}
template <typename... T>
@@ -905,3 +922,243 @@ void test_dependent_param(T t, U) {
co_return 42;
}
template void test_dependent_param(NoCopy<0>, NoCopy<1>); // expected-note {{requested here}}
+
+namespace CoroHandleMemberFunctionTest {
+struct CoroMemberTag {};
+struct BadCoroMemberTag {};
+
+template <class T, class U>
+constexpr bool IsSameV = false;
+template <class T>
+constexpr bool IsSameV<T, T> = true;
+
+template <class T>
+struct TypeTest {
+ template <class U>
+ static constexpr bool IsSame = IsSameV<T, U>;
+
+ template <class... Args>
+ static constexpr bool MatchesArgs = IsSameV<T,
+ std::experimental::coroutine_traits<CoroMemberTag, Args...>>;
+};
+
+template <class T>
+struct AwaitReturnsType {
+ bool await_ready() const;
+ void await_suspend(...) const;
+ T await_resume() const;
+};
+
+template <class... CoroTraitsArgs>
+struct CoroMemberPromise {
+ using TraitsT = std::experimental::coroutine_traits<CoroTraitsArgs...>;
+ using TypeTestT = TypeTest<TraitsT>;
+ using AwaitTestT = AwaitReturnsType<TypeTestT>;
+
+ CoroMemberTag get_return_object();
+ suspend_always initial_suspend();
+ suspend_always final_suspend();
+
+ AwaitTestT yield_value(int);
+
+ void return_void();
+ void unhandled_exception();
+};
+
+} // namespace CoroHandleMemberFunctionTest
+
+template <class... Args>
+struct ::std::experimental::coroutine_traits<CoroHandleMemberFunctionTest::CoroMemberTag, Args...> {
+ using promise_type = CoroHandleMemberFunctionTest::CoroMemberPromise<CoroHandleMemberFunctionTest::CoroMemberTag, Args...>;
+};
+
+namespace CoroHandleMemberFunctionTest {
+struct TestType {
+
+ CoroMemberTag test_qual() {
+ auto TC = co_yield 0;
+ static_assert(TC.MatchesArgs<TestType &>, "");
+ static_assert(!TC.MatchesArgs<TestType>, "");
+ static_assert(!TC.MatchesArgs<TestType *>, "");
+ }
+
+ CoroMemberTag test_sanity(int *) const {
+ auto TC = co_yield 0;
+ static_assert(TC.MatchesArgs<const TestType &>, ""); // expected-error {{static_assert failed}}
+ static_assert(TC.MatchesArgs<const TestType &>, ""); // expected-error {{static_assert failed}}
+ static_assert(TC.MatchesArgs<const TestType &, int *>, "");
+ }
+
+ CoroMemberTag test_qual(int *, const float &&, volatile void *volatile) const {
+ auto TC = co_yield 0;
+ static_assert(TC.MatchesArgs<const TestType &, int *, const float &&, volatile void *volatile>, "");
+ }
+
+ CoroMemberTag test_qual() const volatile {
+ auto TC = co_yield 0;
+ static_assert(TC.MatchesArgs<const volatile TestType &>, "");
+ }
+
+ CoroMemberTag test_ref_qual() & {
+ auto TC = co_yield 0;
+ static_assert(TC.MatchesArgs<TestType &>, "");
+ }
+ CoroMemberTag test_ref_qual() const & {
+ auto TC = co_yield 0;
+ static_assert(TC.MatchesArgs<TestType const &>, "");
+ }
+ CoroMemberTag test_ref_qual() && {
+ auto TC = co_yield 0;
+ static_assert(TC.MatchesArgs<TestType &&>, "");
+ }
+ CoroMemberTag test_ref_qual(const char *&) const volatile && {
+ auto TC = co_yield 0;
+ static_assert(TC.MatchesArgs<TestType const volatile &&, const char *&>, "");
+ }
+
+ CoroMemberTag test_args(int) {
+ auto TC = co_yield 0;
+ static_assert(TC.MatchesArgs<TestType &, int>, "");
+ }
+ CoroMemberTag test_args(int, long &, void *) const {
+ auto TC = co_yield 0;
+ static_assert(TC.MatchesArgs<TestType const &, int, long &, void *>, "");
+ }
+
+ template <class... Args>
+ CoroMemberTag test_member_template(Args...) const && {
+ auto TC = co_yield 0;
+ static_assert(TC.template MatchesArgs<TestType const &&, Args...>, "");
+ }
+
+ static CoroMemberTag test_static() {
+ auto TC = co_yield 0;
+ static_assert(TC.MatchesArgs<>, "");
+ static_assert(!TC.MatchesArgs<TestType>, "");
+ static_assert(!TC.MatchesArgs<TestType &>, "");
+ static_assert(!TC.MatchesArgs<TestType *>, "");
+ }
+
+ static CoroMemberTag test_static(volatile void *const, char &&) {
+ auto TC = co_yield 0;
+ static_assert(TC.MatchesArgs<volatile void *const, char &&>, "");
+ }
+
+ template <class Dummy>
+ static CoroMemberTag test_static_template(const char *volatile &, unsigned) {
+ auto TC = co_yield 0;
+ using TCT = decltype(TC);
+ static_assert(TCT::MatchesArgs<const char *volatile &, unsigned>, "");
+ static_assert(!TCT::MatchesArgs<TestType &, const char *volatile &, unsigned>, "");
+ }
+
+ BadCoroMemberTag test_diagnostics() {
+ // expected-error at -1 {{this function cannot be a coroutine: 'std::experimental::coroutine_traits<CoroHandleMemberFunctionTest::BadCoroMemberTag, CoroHandleMemberFunctionTest::TestType &>' has no member named 'promise_type'}}
+ co_return;
+ }
+ BadCoroMemberTag test_diagnostics(int) const && {
+ // expected-error at -1 {{this function cannot be a coroutine: 'std::experimental::coroutine_traits<CoroHandleMemberFunctionTest::BadCoroMemberTag, const CoroHandleMemberFunctionTest::TestType &&, int>' has no member named 'promise_type'}}
+ co_return;
+ }
+
+ static BadCoroMemberTag test_static_diagnostics(long *) {
+ // expected-error at -1 {{this function cannot be a coroutine: 'std::experimental::coroutine_traits<CoroHandleMemberFunctionTest::BadCoroMemberTag, long *>' has no member named 'promise_type'}}
+ co_return;
+ }
+};
+
+template CoroMemberTag TestType::test_member_template(long, const char *) const &&;
+template CoroMemberTag TestType::test_static_template<void>(const char *volatile &, unsigned);
+
+template <class... Args>
+struct DepTestType {
+
+ CoroMemberTag test_sanity(int *) const {
+ auto TC = co_yield 0;
+ static_assert(TC.template MatchesArgs<const DepTestType &>, ""); // expected-error {{static_assert failed}}
+ static_assert(TC.template MatchesArgs<>, ""); // expected-error {{static_assert failed}}
+ static_assert(TC.template MatchesArgs<const DepTestType &, int *>, "");
+ }
+
+ CoroMemberTag test_qual() {
+ auto TC = co_yield 0;
+ static_assert(TC.template MatchesArgs<DepTestType &>, "");
+ static_assert(!TC.template MatchesArgs<DepTestType>, "");
+ static_assert(!TC.template MatchesArgs<DepTestType *>, "");
+ }
+
+ CoroMemberTag test_qual(int *, const float &&, volatile void *volatile) const {
+ auto TC = co_yield 0;
+ static_assert(TC.template MatchesArgs<const DepTestType &, int *, const float &&, volatile void *volatile>, "");
+ }
+
+ CoroMemberTag test_qual() const volatile {
+ auto TC = co_yield 0;
+ static_assert(TC.template MatchesArgs<const volatile DepTestType &>, "");
+ }
+
+ CoroMemberTag test_ref_qual() & {
+ auto TC = co_yield 0;
+ static_assert(TC.template MatchesArgs<DepTestType &>, "");
+ }
+ CoroMemberTag test_ref_qual() const & {
+ auto TC = co_yield 0;
+ static_assert(TC.template MatchesArgs<DepTestType const &>, "");
+ }
+ CoroMemberTag test_ref_qual() && {
+ auto TC = co_yield 0;
+ static_assert(TC.template MatchesArgs<DepTestType &&>, "");
+ }
+ CoroMemberTag test_ref_qual(const char *&) const volatile && {
+ auto TC = co_yield 0;
+ static_assert(TC.template MatchesArgs<DepTestType const volatile &&, const char *&>, "");
+ }
+
+ CoroMemberTag test_args(int) {
+ auto TC = co_yield 0;
+ static_assert(TC.template MatchesArgs<DepTestType &, int>, "");
+ }
+ CoroMemberTag test_args(int, long &, void *) const {
+ auto TC = co_yield 0;
+ static_assert(TC.template MatchesArgs<DepTestType const &, int, long &, void *>, "");
+ }
+
+ template <class... UArgs>
+ CoroMemberTag test_member_template(UArgs...) const && {
+ auto TC = co_yield 0;
+ static_assert(TC.template MatchesArgs<DepTestType const &&, UArgs...>, "");
+ }
+
+ static CoroMemberTag test_static() {
+ auto TC = co_yield 0;
+ using TCT = decltype(TC);
+ static_assert(TCT::MatchesArgs<>, "");
+ static_assert(!TCT::MatchesArgs<DepTestType>, "");
+ static_assert(!TCT::MatchesArgs<DepTestType &>, "");
+ static_assert(!TCT::MatchesArgs<DepTestType *>, "");
+
+ // Ensure diagnostics are actually being generated here
+ static_assert(TCT::MatchesArgs<int>, ""); // expected-error {{static_assert failed}}
+ }
+
+ static CoroMemberTag test_static(volatile void *const, char &&) {
+ auto TC = co_yield 0;
+ using TCT = decltype(TC);
+ static_assert(TCT::MatchesArgs<volatile void *const, char &&>, "");
+ }
+
+ template <class Dummy>
+ static CoroMemberTag test_static_template(const char *volatile &, unsigned) {
+ auto TC = co_yield 0;
+ using TCT = decltype(TC);
+ static_assert(TCT::MatchesArgs<const char *volatile &, unsigned>, "");
+ static_assert(!TCT::MatchesArgs<DepTestType &, const char *volatile &, unsigned>, "");
+ }
+};
+
+template struct DepTestType<int>; // expected-note {{requested here}}
+template CoroMemberTag DepTestType<int>::test_member_template(long, const char *) const &&;
+
+template CoroMemberTag DepTestType<int>::test_static_template<void>(const char *volatile &, unsigned);
+
+} // namespace CoroHandleMemberFunctionTest
More information about the cfe-commits
mailing list