Skip to content

Commit bd541b3

Browse files
committed
write_demangled_name supports libc++ + clang-cl
The current implementation assumes whenever we're on an FMT_MSC_VERSION compiler, the standard library is MSVC's STL. However, with clang-cl we have the possibility of using LLVM libc++ instead of MSVC STL. In that scenario, the previous implementation produced the wrong demangled names for RTTI types. This patch detects the different combinations, and combines the existing demangling implementations to produce the correct names and make all tests pass on libc++ + clang-cl.
1 parent 4801f54 commit bd541b3

File tree

1 file changed

+37
-22
lines changed

1 file changed

+37
-22
lines changed

include/fmt/std.h

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -139,19 +139,7 @@ template <typename Variant, typename Char> class is_variant_formattable {
139139
#endif // FMT_CPP_LIB_VARIANT
140140

141141
#if FMT_USE_RTTI
142-
143-
template <typename OutputIt>
144-
auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt {
145-
# ifdef FMT_HAS_ABI_CXA_DEMANGLE
146-
int status = 0;
147-
size_t size = 0;
148-
std::unique_ptr<char, void (*)(void*)> demangled_name_ptr(
149-
abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free);
150-
151-
string_view demangled_name_view;
152-
if (demangled_name_ptr) {
153-
demangled_name_view = demangled_name_ptr.get();
154-
142+
string_view normalize_libcxx_inline_namespaces(string_view demangled_name_view, char* begin) {
155143
// Normalization of stdlib inline namespace names.
156144
// libc++ inline namespaces.
157145
// std::__1::* -> std::*
@@ -160,13 +148,12 @@ auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt {
160148
// std::__cxx11::* -> std::*
161149
// std::filesystem::__cxx11::* -> std::filesystem::*
162150
if (demangled_name_view.starts_with("std::")) {
163-
char* begin = demangled_name_ptr.get();
164151
char* to = begin + 5; // std::
165-
for (char *from = to, *end = begin + demangled_name_view.size();
152+
for (const char *from = to, *end = begin + demangled_name_view.size();
166153
from < end;) {
167154
// This is safe, because demangled_name is NUL-terminated.
168155
if (from[0] == '_' && from[1] == '_') {
169-
char* next = from + 1;
156+
const char* next = from + 1;
170157
while (next < end && *next != ':') next++;
171158
if (next[0] == ':' && next[1] == ':') {
172159
from = next + 2;
@@ -177,12 +164,12 @@ auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt {
177164
}
178165
demangled_name_view = {begin, detail::to_unsigned(to - begin)};
179166
}
180-
} else {
181-
demangled_name_view = string_view(ti.name());
182-
}
183-
return detail::write_bytes<char>(out, demangled_name_view);
184-
# elif FMT_MSC_VERSION
185-
const string_view demangled_name(ti.name());
167+
return demangled_name_view;
168+
}
169+
170+
template <class OutputIt>
171+
auto normalize_msvc_abi_name(string_view abi_name_view, OutputIt out) {
172+
const string_view demangled_name(abi_name_view);
186173
for (size_t i = 0; i < demangled_name.size(); ++i) {
187174
auto sub = demangled_name;
188175
sub.remove_prefix(i);
@@ -201,6 +188,34 @@ auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt {
201188
if (*sub.begin() != ' ') *out++ = *sub.begin();
202189
}
203190
return out;
191+
}
192+
193+
template <typename OutputIt>
194+
auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt {
195+
# ifdef FMT_HAS_ABI_CXA_DEMANGLE
196+
int status = 0;
197+
size_t size = 0;
198+
std::unique_ptr<char, void (*)(void*)> demangled_name_ptr(
199+
abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free);
200+
201+
string_view demangled_name_view;
202+
if (demangled_name_ptr) {
203+
demangled_name_view = normalize_libcxx_inline_namespaces(demangled_name_ptr.get(), demangled_name_ptr.get());
204+
} else {
205+
demangled_name_view = string_view(ti.name());
206+
}
207+
return detail::write_bytes<char>(out, demangled_name_view);
208+
# elif FMT_MSC_VERSION && defined(_MSVC_STL_UPDATE)
209+
return normalize_msvc_abi_name(ti.name(), out);
210+
# elif FMT_MSC_VERSION && defined(_LIBCPP_VERSION)
211+
const string_view demangled_name(ti.name());
212+
std::string name_copy(demangled_name.size(), '\0');
213+
// normalize_msvc_abi_name removes class, struct, union etc that MSVC has in front of types
214+
name_copy.erase(normalize_msvc_abi_name(demangled_name, name_copy.begin()), name_copy.end());
215+
// normalize_libcxx_inline_namespaces removes the inline __1, __2, etc namespaces libc++ uses for ABI versioning
216+
// On MSVC ABI + libc++ environments, we need to eliminate both of them.
217+
const string_view normalized_name = normalize_libcxx_inline_namespaces(name_copy, name_copy.data());
218+
return detail::write_bytes<char>(out, normalized_name);
204219
# else
205220
return detail::write_bytes<char>(out, string_view(ti.name()));
206221
# endif

0 commit comments

Comments
 (0)