[Lldb-commits] [lldb] [lldb] Support MSVC STL/PDB in coroutine_handle formatter (PR #194941)
via lldb-commits
lldb-commits at lists.llvm.org
Wed Apr 29 12:50:34 PDT 2026
https://github.com/Nerixyz created https://github.com/llvm/llvm-project/pull/194941
This adds support for the `coroutine_handle` from MSVC's STL with both DWARF and PDB when compiled with (a recent) Clang.
The two issues with PDB are missing template parameters and missing info about artificial variables. If we're running in an MSVC environment, we ignore the artificial flag.
Get the promise type from a `std::coroutine_handle` is a bit awkward. It doesn't include a typedef. The promise type is only reflected in the `promise()` method, so that's used to find the type.
The type names for the symbols are a bit different, so I updated the regex in the test.
This doesn't work for executables compiled with MSVC.
>From e66e0bdaf11130c382c7f77834ad4a7f07bf29f2 Mon Sep 17 00:00:00 2001
From: Nerixyz <nerixdev at outlook.de>
Date: Wed, 29 Apr 2026 21:37:53 +0200
Subject: [PATCH] [lldb] Support MSVC STL in coroutine_handle formatter
---
.../Plugins/Language/CPlusPlus/Coroutines.cpp | 47 +++++++++++++++----
.../generic/coroutine_handle/Makefile | 10 +---
.../coroutine_handle/TestCoroutineHandle.py | 12 ++++-
.../generic/coroutine_handle/main.cpp | 12 +----
4 files changed, 50 insertions(+), 31 deletions(-)
diff --git a/lldb/source/Plugins/Language/CPlusPlus/Coroutines.cpp b/lldb/source/Plugins/Language/CPlusPlus/Coroutines.cpp
index 4cca7aa46abf9..9f5aa7a209039 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/Coroutines.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/Coroutines.cpp
@@ -63,7 +63,8 @@ static Function *ExtractDestroyFunction(lldb::TargetSP target_sp,
// clang generates aritifical `__promise` and `__coro_frame` variables inside
// the destroy function. Look for those variables and extract their type.
static CompilerType InferArtificialCoroType(Function *destroy_func,
- ConstString var_name) {
+ ConstString var_name,
+ bool is_msvc) {
if (!destroy_func)
return {};
@@ -73,7 +74,7 @@ static CompilerType InferArtificialCoroType(Function *destroy_func,
auto var = variable_list->FindVariable(var_name);
if (!var)
return {};
- if (!var->IsArtificial())
+ if (!var->IsArtificial() && !is_msvc)
return {};
Type *promise_type = var->GetType();
@@ -82,6 +83,31 @@ static CompilerType InferArtificialCoroType(Function *destroy_func,
return promise_type->GetForwardCompilerType();
}
+static CompilerType GetPromiseType(CompilerType handle_type, bool is_msvc) {
+ if (!handle_type)
+ return {};
+
+ CompilerType template_arg = handle_type.GetTypeTemplateArgument(0);
+ if (template_arg || !is_msvc)
+ return template_arg;
+
+ // No template argument, this might be PDB. std::coroutine_handle<Promise>
+ // doesn't have a typedef inside - try to find `Promise& promise()` and use
+ // the return type.
+ size_t n_methods = handle_type.GetNumMemberFunctions();
+ for (size_t i = 0; i < n_methods; ++i) {
+ TypeMemberFunctionImpl fn = handle_type.GetMemberFunctionAtIndex(i);
+ if (fn.GetName() == "promise") {
+ CompilerType return_ty = fn.GetReturnType().GetPointeeType();
+ if (return_ty)
+ return return_ty;
+ break; // Return void here to infer the type from __promise.
+ }
+ }
+
+ return handle_type.GetBasicTypeFromAST(lldb::eBasicTypeVoid);
+}
+
bool lldb_private::formatters::StdlibCoroutineHandleSummaryProvider(
ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
lldb::addr_t frame_ptr_addr =
@@ -138,26 +164,27 @@ lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd::Update() {
if (!ast_ctx)
return lldb::ChildCacheState::eRefetch;
+ bool is_msvc =
+ target_sp->GetArchitecture().GetTriple().isWindowsMSVCEnvironment();
+
// Determine the coroutine frame type and the promise type. Fall back
// to `void`, since even the pointer itself might be useful, even if the
// type inference failed.
Function *destroy_func = ExtractDestroyFunction(target_sp, frame_ptr_addr);
CompilerType void_type = ast_ctx->GetBasicType(lldb::eBasicTypeVoid);
- CompilerType promise_type;
- if (CompilerType template_arg =
- valobj_sp->GetCompilerType().GetTypeTemplateArgument(0))
- promise_type = std::move(template_arg);
+ CompilerType promise_type =
+ GetPromiseType(valobj_sp->GetCompilerType(), is_msvc);
if (promise_type.IsVoidType()) {
// Try to infer the promise_type if it was type-erased
if (destroy_func) {
- if (CompilerType inferred_type =
- InferArtificialCoroType(destroy_func, ConstString("__promise"))) {
+ if (CompilerType inferred_type = InferArtificialCoroType(
+ destroy_func, ConstString("__promise"), is_msvc)) {
promise_type = inferred_type;
}
}
}
- CompilerType coro_frame_type =
- InferArtificialCoroType(destroy_func, ConstString("__coro_frame"));
+ CompilerType coro_frame_type = InferArtificialCoroType(
+ destroy_func, ConstString("__coro_frame"), is_msvc);
if (!coro_frame_type)
coro_frame_type = void_type;
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/coroutine_handle/Makefile b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/coroutine_handle/Makefile
index 3e5da0ecae669..4f79c0a900c3a 100644
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/coroutine_handle/Makefile
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/coroutine_handle/Makefile
@@ -1,12 +1,4 @@
CXX_SOURCES := main.cpp
-CFLAGS_EXTRAS := -std=c++20
-
-ifeq "1" "$(USE_LIBSTDCPP)"
- CFLAGS_EXTRAS += -DUSE_LIBSTDCPP
-endif
-
-ifeq "1" "$(USE_LIBCPP)"
- CFLAGS_EXTRAS += -DUSE_LIBCPP
-endif
+CXXFLAGS_EXTRAS := -std=c++20
include Makefile.rules
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/coroutine_handle/TestCoroutineHandle.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/coroutine_handle/TestCoroutineHandle.py
index 21a5c6ec792d9..f24be2550393a 100644
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/coroutine_handle/TestCoroutineHandle.py
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/coroutine_handle/TestCoroutineHandle.py
@@ -10,6 +10,7 @@
class TestCoroutineHandle(TestBase):
SHARED_BUILD_TESTCASE = False
+ TEST_WITH_PDB_DEBUG_INFO = True
def do_test(self):
"""Test std::coroutine_handle is displayed correctly."""
@@ -18,8 +19,11 @@ def do_test(self):
# Clang <= 20 used to also name the resume/destroy functions
# as `my_generator_func`.
# Never versions of clang name the clones as `.resume`/`.destroy`.
+ # On Windows with DWARF, we get:
+ # `struct int_generator my_generator_func(void) at main.cpp:{line}`
+ # and with PDB, we get `my_generator_func at main.cpp:{line}`.
test_generator_func_ptr_re = re.compile(
- r"^\(a.out`my_generator_func\(\)( \(\..*\))? at main.cpp:[0-9]*\)$"
+ r"^\(a.out`(struct int_generator )?my_generator_func(\((void)?\))?( \(\..*\))? at main.cpp:[0-9]*\)$"
)
# Run until the initial suspension point
@@ -172,3 +176,9 @@ def test_libstdcpp(self):
def test_libcpp(self):
self.build(dictionary={"USE_LIBCPP": 1})
self.do_test()
+
+ @add_test_categories(["msvcstl"])
+ def test_msvcstl(self):
+ # No flags, because the "msvcstl" category checks that the MSVC STL is used by default.
+ self.build()
+ self.do_test()
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/coroutine_handle/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/coroutine_handle/main.cpp
index aaf76e0d8d99a..774dfe965d58e 100644
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/coroutine_handle/main.cpp
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/coroutine_handle/main.cpp
@@ -1,14 +1,4 @@
-#if defined(USE_LIBSTDCPP)
-#include <bits/c++config.h>
-// glibc++ >= 11 and c++20
-#if defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE >= 11
-#include <coroutine>
-#define HAS_CPP_COROUTINES 1
-#endif
-#endif
-
-// libc++ always has 'coroutine' feature.
-#if defined(USE_LIBCPP)
+#if __has_include(<coroutine>)
#include <coroutine>
#define HAS_CPP_COROUTINES 1
#endif
More information about the lldb-commits
mailing list