Conformant libc++21 formatter refactor (boundary widening) [WIP]#41072
Conformant libc++21 formatter refactor (boundary widening) [WIP]#41072benhillis wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR explores a C++20/23-conformant approach to formatting localized messages in the presence of libc++21’s deleted cross-encoding std::formatter specializations by removing the repo’s narrow→wide formatters and instead widening narrow arguments at a single boundary.
Changes:
- Updated the localization header generator to route every
Message*()formatting argument throughwsl::shared::string::LocalizationArg(). - Introduced
LocalizationArg()and removed the repo’sstd::formatter<char*/*, wchar_t>-style specializations, plus updated several call sites to explicitly widen narrow strings where needed. - Bumped
Microsoft.WSL.LinuxSdkfrom1.20.0to1.23.0.
Reviewed changes
Copilot reviewed 30 out of 30 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| tools/generateLocalizationHeader.ps1 | Routes generated Message*() formatting arguments through LocalizationArg() before std::vformat. |
| src/shared/inc/stringshared.h | Adds LocalizationArg() boundary helper; removes narrow→wide formatter specializations; updates source_location wide formatting. |
| src/windows/common/ExecutionContext.h | Normalizes user-error message conversion via ToUserErrorMessage() instead of formatting-based coercion. |
| src/windows/wslcsession/WSLCVolumes.cpp | Explicitly widens narrow args passed into localized warnings/errors. |
| src/windows/wslcsession/WSLCVirtualMachine.cpp | Explicitly widens narrow port-string argument for localized error. |
| src/windows/wslcsession/WSLCVhdVolume.cpp | Explicitly widens several narrow args in localized errors/warnings. |
| src/windows/wslcsession/WSLCSession.cpp | Widens narrow identifiers/names passed into localized user errors. |
| src/windows/wslcsession/WSLCGuestVolume.cpp | Widens narrow option strings used in localized error paths. |
| src/windows/wslcsession/WSLCContainer.cpp | Widens various narrow IDs/names used in localized user errors/warnings. |
| src/windows/wslcsession/OptionParser.cpp | Widens option key/value strings for localized user errors. |
| src/windows/wslc/tasks/VolumeTasks.cpp | Widens volume names printed via localized messages. |
| src/windows/wslc/tasks/NetworkTasks.cpp | Widens network names printed via localized messages. |
| src/windows/wslc/tasks/ImageTasks.cpp | Widens image IDs/refs for localized output; widens ID printing in list path. |
| src/windows/wslc/tasks/ContainerTasks.cpp | Widens container IDs printed via localized messages. |
| src/windows/wslc/services/SessionService.cpp | Widens command name passed into localized launch-failure error. |
| src/windows/wslc/services/ImageService.cpp | Widens image reference passed into localized tag-format error. |
| src/windows/wslc/services/ImageProgressCallback.cpp | Fixes direct wide std::format uses by widening LPCSTR inputs. |
| src/windows/wslc/commands/VersionCommand.cpp | Fixes wide std::format call by widening WSL_PACKAGE_VERSION. |
| src/windows/service/exe/WslCoreVm.cpp | Widens enum-to-string values before passing to localized warning. |
| src/windows/service/exe/LxssUserSession.cpp | Widens JSON parse error text; adjusts error-string handling for localized warning. |
| src/windows/common/wslutil.cpp | Widens Windows version / IP / image strings passed into localized messages. |
| src/windows/common/WslCoreConfig.cpp | Widens config key/value strings passed into localized warnings. |
| src/windows/common/WslClient.cpp | Widens strerror/version/build-info narrow inputs used in localized output. |
| src/windows/common/string.cpp | Ensures hex-string invalid input is widened appropriately for localized error formatting. |
| src/windows/common/notifications.cpp | Widens binaryName passed into localized toast content. |
| src/windows/common/ConsommeNetworking.cpp | Widens host address string in wide std::format call. |
| src/shared/inc/JsonUtils.h | Widens exception message for localized invalid-JSON user error on Windows. |
| src/shared/configfile/configfile.h | Widens enum-parse warning arguments before calling localization. |
| src/shared/configfile/configfile.cpp | Widens multiple config warning arguments before calling localization. |
| packages.config | Updates Linux SDK NuGet version to 1.23.0 (and introduces a leading BOM). |
| EMIT_USER_WARNING(Localization::MessageConfigInvalidBoolean( | ||
| wsl::shared::string::MultiByteToWide(value), wsl::shared::string::MultiByteToWide(name), filePath, fileLine)); | ||
| return false; |
| EMIT_USER_WARNING(Localization::MessageConfigInvalidInteger( | ||
| wsl::shared::string::MultiByteToWide(value), wsl::shared::string::MultiByteToWide(name), filePath, fileLine)); | ||
| return false; |
| EMIT_USER_WARNING(wsl::shared::Localization::MessageInvalidNumberString( | ||
| wsl::shared::string::MultiByteToWide(value), wsl::shared::string::MultiByteToWide(name), filePath, fileLine)); | ||
| return false; |
| EMIT_USER_WARNING(Localization::MessageConfigKeyDuplicated( | ||
| wsl::shared::string::MultiByteToWide(name), | ||
| fileName, | ||
| line, | ||
| wsl::shared::string::MultiByteToWide(m_parseResult->first), | ||
| fileName, | ||
| m_parseResult->second)); |
| EMIT_USER_WARNING(Localization::MessageConfigUnknownKey(wsl::shared::string::MultiByteToWide(keyName), filePath, fileLine)); | ||
| return; |
| EMIT_USER_WARNING(wsl::shared::Localization::MessageConfigInvalidEnum( | ||
| wsl::shared::string::MultiByteToWide(value), | ||
| wsl::shared::string::MultiByteToWide(name), | ||
| fileName, | ||
| line, | ||
| wsl::shared::string::MultiByteToWide(validValues.str()))); |
| if constexpr (std::is_same_v<std::remove_cvref_t<T>, std::string> || std::is_same_v<std::remove_cvref_t<T>, std::string_view>) | ||
| { | ||
| return MultiByteToWide(std::string{value}); | ||
| } |
| # Widen narrow arguments once into lifetime-extended locals so make_[w]format_args (which | ||
| # requires lvalues) can bind to them. LocalizationArg is identity on non-narrow types. |
| #pragma once | ||
|
|
||
| #include "wslutil.h" | ||
|
|
| @@ -1,4 +1,4 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
…++ 21) libc++ 21 (Linux SDK 1.23.0) and current MSVC STL delete the std::formatter<char*/const char*/std::string, wchar_t> specializations per C++23 [format.formatter.spec], which collides with WSL's own char->wide formatter specializations in stringshared.h. Remove those specializations and widen narrow arguments at the format boundary instead: - stringshared.h: drop the char->wide formatter specializations; add a WideFormatArg() boundary helper that widens narrow args (char*, const char*, char[N], std::string/std::string_view) to wide, identity otherwise. On non-Windows it is always identity. - generateLocalizationHeader.ps1: wrap every Message*() argument with WideFormatArg via lifetime-extended locals, so localization call sites need no per-site changes. - ExecutionContext.h: normalize narrow user-error messages to wide via ToUserErrorMessage() in the THROW_HR_WITH_USER_ERROR macros. - Fix the remaining direct std::format(L"...") / reporter sinks that pass narrow args (ConsommeNetworking, VersionCommand, ImageProgressCallback, ImageTasks, WslClient version macros, tests). Prefer compile-time STRING_TO_WIDE_STRING for known-narrow literals. - packages.config: bump Microsoft.WSL.LinuxSdk to 1.23.0.
cf8fb35 to
47a953e
Compare
| inline std::wstring ToUserErrorMessage(std::string_view message) | ||
| { | ||
| return wsl::shared::string::MultiByteToWide(std::string{message}); | ||
| } |
| # Widen narrow arguments once into lifetime-extended locals so make_[w]format_args (which | ||
| # requires lvalues) can bind to them. LocalizationArg is identity on non-narrow types. |
| @@ -1,4 +1,4 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
|
Closing in favor of #41071. The fully-conformant approach (removing the char->wide formatter specializations and widening at every boundary) touches ~22 files, because MSVC and libc++ 21 both delete those specializations, so every direct wide std::format narrow call site must change. The minimal Windows-guarded formatter in #41071 is the better tradeoff. |
Draft / WIP. Follow-up to #41071 (which ships the minimal
#ifdef WIN32guard as the shipping fix).This branch explores the fully conformant alternative: instead of keeping the
formatter<char*, wchar_t>/formatter<std::string, wchar_t>specializations (deleted in C++20/23 [format.formatter.spec] and shipped as deleted specializations in libc++ 20+), it removes them and widens narrow arguments explicitly.Approach
The key insight is a single boundary helper rather than per-call-site churn:
wsl::shared::string::LocalizationArg()(stringshared.h) widens narrow string args (const char*,char[N],std::string,std::string_view) towchar_ton Windows, and is an identity forward on Linux (where messages are already narrow).generateLocalizationHeader.ps1) routes everyMessage*()argument through it, sostd::vformat(message, ARGS(LocalizationArg(arg)...)).This fixes all product code (common, wslc, wslcsession, service.exe) through one generator change.
Remaining work
std::format(L"...", narrowArg)sites that do not go throughLocalization(primarilytest/windowsunit tests) still rely on the removed specializations and need conversion.