[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.

llvm-svn: 307513
This commit is contained in:
Eric Fiselier 2017-07-10 01:27:22 +00:00
parent ac43c1bb87
commit 166c6e6f05
2 changed files with 300 additions and 19 deletions

View File

@ -43,9 +43,10 @@ static bool lookupMember(Sema &S, const char *Name, CXXRecordDecl *RD,
/// 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 &S, const FunctionProtoType *FnType,
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 &S, VarDecl *Promise,
VarDecl *Sema::buildCoroutinePromise(SourceLocation Loc) {
assert(isa<FunctionDecl>(CurContext) && "not in a function scope");
auto *FD = cast<FunctionDecl>(CurContext);
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()
? Context.DependentTy
: lookupPromiseType(*this, FD->getType()->castAs<FunctionProtoType>(),
Loc, FD->getLocation());
QualType T = FD->getType()->isDependentType() || IsThisDependentType
? Context.DependentTy
: lookupPromiseType(*this, FD, Loc);
if (T.isNull())
return nullptr;

View File

@ -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@-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@-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@-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