forked from OSchip/llvm-project
[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:
parent
ac43c1bb87
commit
166c6e6f05
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue