blob: 077f69ea6598d812349f9105da1f65f706d60224 [file] [log] [blame]
// RUN: rm -fR %t
// RUN: split-file %s %t
// RUN: cd %t
// RUN: %clang_cc1 -std=c++20 -xc++ -emit-module -fmodules foo.cppmap -fmodule-name=foo -fmodule-name=foo -o foo.pcm
// RUN: %clang_cc1 -std=c++20 -xc++ -emit-module -fmodules bar.cppmap -fmodule-name=bar -o bar.pcm
// RUN: %clang_cc1 -std=c++20 -xc++ -emit-module -fmodules experiment_context.cppmap -fmodule-name=experiment_context -fmodule-file=foo.pcm -fmodule-file=bar.pcm -o experiment_context.pcm
// RUN: %clang_cc1 -verify -std=c++20 -xc++ -fmodule-file=experiment_context.pcm experiment_context_test.cc -o experiment_context_test.o
// https://github.com/llvm/llvm-project/issues/141582
//--- bar.cppmap
module "bar" {
export *
header "co.h"
}
//--- foo.cppmap
module "foo" {
export *
header "co.h"
}
//--- experiment_context.cppmap
module "experiment_context" {
export *
header "lazy.h"
use "foo"
use "bar"
}
//--- experiment_context_test.cc
// expected-no-diagnostics
#include "lazy.h"
namespace c9 {
template <typename T>
void WaitForCoroutine() {
MakeCo<T>([]() -> Co<void> {
co_return;
});
}
void test() {
c9::WaitForCoroutine<void>();
}
}
//--- lazy.h
#pragma once
#include "co.h"
namespace c9 {
template <typename T, typename F>
Co<T> MakeCo(F f)
{
co_return co_await f();
}
}
inline c9::Co<void> DoNothing() { co_return; }
//--- co.h
#pragma once
namespace std {
template <class _Ret, class... _Args>
struct coroutine_traits {};
template <typename Ret, typename... Args>
requires requires { typename Ret::promise_type; }
struct coroutine_traits<Ret, Args...> {
using promise_type = typename Ret::promise_type;
};
template <typename Promise = void>
struct coroutine_handle;
template <>
struct coroutine_handle<void> {};
template <typename Promise = void>
struct coroutine_handle : coroutine_handle<> {
static coroutine_handle from_address(void *addr);
};
struct suspend_always {
bool await_ready() noexcept;
void await_suspend(coroutine_handle<>) noexcept;
void await_resume() noexcept;
};
} // namespace std
namespace c9 {
template <typename T>
class Co;
namespace internal {
template <typename T>
class CoroutinePromise {
public:
template <typename... Args>
explicit CoroutinePromise(Args&&... args) {
// Ensure that the 'dummy_color' VarDecl referenced by the inner DeclRefExpr
// is the same declaration as the one outside the lambda.
// This is guaranteed because both CoroutinePromise and the lambda's call operator
// (CXXMethodDecl) are loaded from the same module.
const int dummy_color = 1;
[&]{ (void)dummy_color; }();
}
~CoroutinePromise();
void return_void();
auto get_return_object() {
return Co<T>();
}
void unhandled_exception();
std::suspend_always initial_suspend();
struct result_t {
~result_t();
bool await_ready() const noexcept;
void await_suspend(std::coroutine_handle<void>) noexcept;
void await_resume() const noexcept;
};
template <typename U>
result_t await_transform(Co<U> co);
std::suspend_always final_suspend() noexcept;
};
} // namespace internal
template <typename T>
class Co {
public:
using promise_type = internal::CoroutinePromise<void>;
};
class CoIncomingModuleBase {
public:
Co<void> CoAfterFinish() { co_return; }
};
} // namespace c9