From c7ae2a7b183b156f9e8b949313cd9fb885e41d63 Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Fri, 13 Dec 2024 09:48:05 -0500 Subject: [PATCH] Avoid printing enums as characters (#4676) Given code like the following: ``` auto kind = ConversionTarget::Kind{0}; CARBON_CHECK(!loc_id.is_valid(), "hello {0} world", kind); ``` Currently we would print 'hello ', as the check string would be treated as terminating at the '{0}', so it does not print the rest of the string or a newline. This is because ConversionTarget::Kind is an enum with underlying type `int8_t` which is a char, and llvm::formatv does not look if the type is an enum and treat is specially. So it prints it as a char rather than a number, which in this case is a nul terminator. With this change, the '{0}' value will be converted to a larger integer before being passed through to llvm::formatv so that char-sized enums will print as a number, and the result is that we will print 'hello 0 world\n' as the developer intended. --- common/check_internal.h | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/common/check_internal.h b/common/check_internal.h index 83cba46e127d..97161297ed6f 100644 --- a/common/check_internal.h +++ b/common/check_internal.h @@ -43,6 +43,28 @@ CheckCondition(bool condition) const char* condition_str, llvm::StringRef extra_message) -> void; +// Allow converting format values; the default behaviour is to just pass them +// through. +template +auto ConvertFormatValue(T&& t) -> T&& { + return std::forward(t); +} + +// Convert enums to larger integers so that byte-sized enums are not confused +// with being chars and printed as invalid (or nul-terminating) characters. +// Scoped enums are explicitly converted to integers so they can be printed +// without the user writing a cast. +template + requires(std::is_enum_v>) +auto ConvertFormatValue(T&& t) { + if constexpr (std::is_signed_v< + std::underlying_type_t>>) { + return static_cast(t); + } else { + return static_cast(t); + } +} + // Prints a check failure, including rendering any user-provided message using // a format string. // @@ -64,9 +86,10 @@ template (values)...).str()); + CheckFailImpl(Kind.c_str(), File.c_str(), Line, ConditionStr.c_str(), + llvm::formatv(FormatStr.c_str(), + ConvertFormatValue(std::forward(values))...) + .str()); } }