Skip to content

[clang][modules] Fix lambda and its enclosing function are not loaded from same module. #142467

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion clang/include/clang/Serialization/ASTReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -566,7 +566,7 @@ class ASTReader
/// modules. It is used for the following cases:
/// - Lambda inside a template function definition: The main declaration is
/// the enclosing function, and the related declarations are the lambda
/// declarations.
/// call operators.
/// - Friend function defined inside a template CXXRecord declaration: The
/// main declaration is the enclosing record, and the related declarations
/// are the friend functions.
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Serialization/ASTWriterDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1655,7 +1655,7 @@ void ASTDeclWriter::VisitCXXRecordDecl(CXXRecordDecl *D) {
if (auto *FD = llvm::dyn_cast_or_null<FunctionDecl>(D->getDeclContext());
FD && isDefinitionInDependentContext(FD)) {
Writer.RelatedDeclsMap[Writer.GetDeclRef(FD)].push_back(
Writer.GetDeclRef(D));
Writer.GetDeclRef(D->getLambdaCallOperator()));
}
} else {
Record.push_back(CXXRecNotTemplate);
Expand Down
149 changes: 149 additions & 0 deletions clang/test/Headers/crash-instantiated-in-scope-cxx-modules6.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
// 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
Loading