瀏覽代碼

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.
Dana Jansens 1 年之前
父節點
當前提交
c7ae2a7b18
共有 1 個文件被更改,包括 26 次插入3 次删除
  1. 26 3
      common/check_internal.h

+ 26 - 3
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 <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());
   }
 }