[Lldb-commits] [lldb] [lldb] Support MSVC STL/PDB in coroutine_handle formatter (PR #194941)
via lldb-commits
lldb-commits at lists.llvm.org
Wed Aug 19 01:16:06 PDT 2026
https://github.com/Nerixyz updated https://github.com/llvm/llvm-project/pull/194941
>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