Skip to content

Commit 03c7e28

Browse files
authored
write_demangled_name supports libc++ + clang-cl (#4560)
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 47a18b2 commit 03c7e28

File tree

1 file changed

+62
-40
lines changed

1 file changed

+62
-40
lines changed

include/fmt/std.h

Lines changed: 62 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -139,50 +139,39 @@ 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-
155-
// Normalization of stdlib inline namespace names.
156-
// libc++ inline namespaces.
157-
// std::__1::* -> std::*
158-
// std::__1::__fs::* -> std::*
159-
// libstdc++ inline namespaces.
160-
// std::__cxx11::* -> std::*
161-
// std::filesystem::__cxx11::* -> std::filesystem::*
162-
if (demangled_name_view.starts_with("std::")) {
163-
char* begin = demangled_name_ptr.get();
164-
char* to = begin + 5; // std::
165-
for (char *from = to, *end = begin + demangled_name_view.size();
166-
from < end;) {
167-
// This is safe, because demangled_name is NUL-terminated.
168-
if (from[0] == '_' && from[1] == '_') {
169-
char* next = from + 1;
170-
while (next < end && *next != ':') next++;
171-
if (next[0] == ':' && next[1] == ':') {
172-
from = next + 2;
173-
continue;
174-
}
142+
string_view normalize_libcxx_inline_namespaces(string_view demangled_name_view,
143+
char* begin) {
144+
// Normalization of stdlib inline namespace names.
145+
// libc++ inline namespaces.
146+
// std::__1::* -> std::*
147+
// std::__1::__fs::* -> std::*
148+
// libstdc++ inline namespaces.
149+
// std::__cxx11::* -> std::*
150+
// std::filesystem::__cxx11::* -> std::filesystem::*
151+
if (demangled_name_view.starts_with("std::")) {
152+
char* to = begin + 5; // std::
153+
for (const char *from = to, *end = begin + demangled_name_view.size();
154+
from < end;) {
155+
// This is safe, because demangled_name is NUL-terminated.
156+
if (from[0] == '_' && from[1] == '_') {
157+
const char* next = from + 1;
158+
while (next < end && *next != ':') next++;
159+
if (next[0] == ':' && next[1] == ':') {
160+
from = next + 2;
161+
continue;
175162
}
176-
*to++ = *from++;
177163
}
178-
demangled_name_view = {begin, detail::to_unsigned(to - begin)};
164+
*to++ = *from++;
179165
}
180-
} else {
181-
demangled_name_view = string_view(ti.name());
166+
demangled_name_view = {begin, detail::to_unsigned(to - begin)};
182167
}
183-
return detail::write_bytes<char>(out, demangled_name_view);
184-
# elif FMT_MSC_VERSION
185-
const string_view demangled_name(ti.name());
168+
return demangled_name_view;
169+
}
170+
171+
template <class OutputIt>
172+
auto normalize_msvc_abi_name(string_view abi_name_view, OutputIt out)
173+
-> OutputIt {
174+
const string_view demangled_name(abi_name_view);
186175
for (size_t i = 0; i < demangled_name.size(); ++i) {
187176
auto sub = demangled_name;
188177
sub.remove_prefix(i);
@@ -201,6 +190,39 @@ auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt {
201190
if (*sub.begin() != ' ') *out++ = *sub.begin();
202191
}
203192
return out;
193+
}
194+
195+
template <typename OutputIt>
196+
auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt {
197+
# ifdef FMT_HAS_ABI_CXA_DEMANGLE
198+
int status = 0;
199+
size_t size = 0;
200+
std::unique_ptr<char, void (*)(void*)> demangled_name_ptr(
201+
abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free);
202+
203+
string_view demangled_name_view;
204+
if (demangled_name_ptr) {
205+
demangled_name_view = normalize_libcxx_inline_namespaces(
206+
demangled_name_ptr.get(), demangled_name_ptr.get());
207+
} else {
208+
demangled_name_view = string_view(ti.name());
209+
}
210+
return detail::write_bytes<char>(out, demangled_name_view);
211+
# elif FMT_MSC_VERSION && defined(_MSVC_STL_UPDATE)
212+
return normalize_msvc_abi_name(ti.name(), out);
213+
# elif FMT_MSC_VERSION && defined(_LIBCPP_VERSION)
214+
const string_view demangled_name(ti.name());
215+
std::string name_copy(demangled_name.size(), '\0');
216+
// normalize_msvc_abi_name removes class, struct, union etc that MSVC has in
217+
// front of types
218+
name_copy.erase(normalize_msvc_abi_name(demangled_name, name_copy.begin()),
219+
name_copy.end());
220+
// normalize_libcxx_inline_namespaces removes the inline __1, __2, etc
221+
// namespaces libc++ uses for ABI versioning On MSVC ABI + libc++
222+
// environments, we need to eliminate both of them.
223+
const string_view normalized_name =
224+
normalize_libcxx_inline_namespaces(name_copy, name_copy.data());
225+
return detail::write_bytes<char>(out, normalized_name);
204226
# else
205227
return detail::write_bytes<char>(out, string_view(ti.name()));
206228
# endif

0 commit comments

Comments
 (0)