llvm-project/clang/test/Headers/crash-instantiated-in-scope-cxx-modules6.cpp
Haojian Wu 6ca59aae8e
[clang][modules] Fix lambda and its enclosing function are not loaded from same module. (#142467)
This is a follow-up fix to
https://github.com/llvm/llvm-project/pull/109167.

Previously, we stored a mapping between the enclosing function and the
lambda class declaration. When loading the enclosing function, we also
loaded the corresponding lambda class declaration. However, loading the
lambda class declaration does not guarantee that its call operator (a
`CXXMethodDecl`) is loaded as well.
As a result, if the lambda call operator is later loaded from a
different module, we can end up with a `DeclRefExpr` that refers to a
`VarDecl` from a different module — leading to inconsistencies.

To fix this, we should ensure the lambda call operator itself is loaded.

Fixes #141582
2025-06-03 17:41:36 +02:00

150 lines
3.2 KiB
C++

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