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 <the next line>', 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.
This commit is contained in:
Dana Jansens
2024-12-13 14:48:05 +00:00
committed by GitHub
parent aee098b8e2
commit c7ae2a7b18
+26 -3
View File
@@ -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 <typename T>
auto ConvertFormatValue(T&& t) -> T&& {
return std::forward<T>(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 <typename T>
requires(std::is_enum_v<std::remove_reference_t<T>>)
auto ConvertFormatValue(T&& t) {
if constexpr (std::is_signed_v<
std::underlying_type_t<std::remove_reference_t<T>>>) {
return static_cast<int64_t>(t);
} else {
return static_cast<uint64_t>(t);
}
}
// Prints a check failure, including rendering any user-provided message using
// a format string.
//
@@ -64,9 +86,10 @@ template <TemplateString Kind, TemplateString File, int Line,
// at all, just the condition.
CheckFailImpl(Kind.c_str(), File.c_str(), Line, ConditionStr.c_str(), "");
} else {
CheckFailImpl(
Kind.c_str(), File.c_str(), Line, ConditionStr.c_str(),
llvm::formatv(FormatStr.c_str(), std::forward<Ts>(values)...).str());
CheckFailImpl(Kind.c_str(), File.c_str(), Line, ConditionStr.c_str(),
llvm::formatv(FormatStr.c_str(),
ConvertFormatValue(std::forward<Ts>(values))...)
.str());
}
}